summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.editorconfig43
-rw-r--r--.git-blame-ignore-revs4
-rw-r--r--.github/dependabot.yml10
-rw-r--r--.github/linters/.ecrc12
-rw-r--r--.github/linters/.markdown-lint.yml26
-rw-r--r--.github/linters/.yaml-lint.yml9
-rw-r--r--.github/workflows/build.yml155
-rw-r--r--.github/workflows/codeql-analysis.yml46
-rw-r--r--.github/workflows/lint.yml32
-rw-r--r--.github/workflows/oss-fuzz.yml23
-rw-r--r--.github/workflows/super-linter.yml23
-rw-r--r--.gitignore25
-rw-r--r--.gitlab-ci.yml3214
-rw-r--r--.pre-commit-config.yaml58
-rw-r--r--.travis.yml22
-rw-r--r--.yardopts2
-rw-r--r--AUTHORS333
-rw-r--r--CODEOWNERS1
-rw-r--r--CONTRIBUTING.md71
-rw-r--r--Doxyfile2408
-rw-r--r--LEGAL3
-rw-r--r--LICENSE (renamed from MITL)3
-rw-r--r--Makefile6
-rw-r--r--NEWS2
-rw-r--r--README.md56
-rw-r--r--Rakefile138
-rw-r--r--TODO10
-rw-r--r--TODO.md15
-rw-r--r--appveyor.yml53
-rw-r--r--benchmark/bm_ao_render.rb4
-rw-r--r--benchmark/bm_app_lc_fizzbuzz.rb3
-rw-r--r--benchmark/bm_so_lists.rb2
-rw-r--r--benchmark/build_config_boxing.rb28
-rw-r--r--benchmark/build_config_cc.rb13
-rw-r--r--build_config.rb161
-rw-r--r--build_config/ArduinoDue.rb (renamed from examples/targets/build_config_ArduinoDue.rb)27
-rw-r--r--build_config/IntelEdison.rb (renamed from examples/targets/build_config_IntelEdison.rb)10
-rw-r--r--build_config/IntelGalileo.rb (renamed from examples/targets/build_config_IntelGalileo.rb)25
-rw-r--r--build_config/RX630.rb (renamed from examples/targets/build_config_RX630.rb)25
-rw-r--r--build_config/android_arm64_v8a.rb11
-rw-r--r--build_config/android_armeabi.rb11
-rw-r--r--build_config/android_armeabi_v7a_neon_hard.rb (renamed from examples/targets/build_config_android_armeabi_v7a_neon_hard.rb)15
-rw-r--r--build_config/bench.rb11
-rw-r--r--build_config/boxing.rb21
-rw-r--r--build_config/chipKITMax32.rb (renamed from examples/targets/build_config_chipKITMax32.rb)25
-rw-r--r--build_config/ci/gcc-clang.rb (renamed from appveyor_config.rb)30
-rw-r--r--build_config/ci/msvc.rb20
-rw-r--r--build_config/clang-asan.rb11
-rw-r--r--build_config/cross-32bit.rb14
-rw-r--r--build_config/cross-mingw.rb14
-rw-r--r--build_config/default.rb80
-rw-r--r--build_config/dreamcast_shelf.rb119
-rw-r--r--build_config/gameboyadvance.rb73
-rw-r--r--build_config/host-cxx.rb12
-rw-r--r--build_config/host-debug.rb20
-rw-r--r--build_config/host-f32.rb14
-rw-r--r--build_config/host-gprof.rb14
-rw-r--r--build_config/host-m32.rb15
-rw-r--r--build_config/host-nofloat.rb22
-rw-r--r--build_config/host-shared.rb36
-rw-r--r--build_config/minimal.rb4
-rw-r--r--build_config/mrbc.rb11
-rw-r--r--build_config/nintendo_switch.rb73
-rw-r--r--codespell.txt17
-rw-r--r--doc/guides/compile.md440
-rw-r--r--doc/guides/debugger.md21
-rw-r--r--doc/guides/gc-arena-howto.md14
-rw-r--r--doc/guides/link.md83
-rw-r--r--doc/guides/mrbconf.md132
-rw-r--r--doc/guides/mrbgems.md280
-rw-r--r--doc/guides/symbol.md83
-rw-r--r--doc/limitations.md173
-rw-r--r--doc/mruby3.md158
-rw-r--r--doc/mruby_logo_red_icon.pngbin0 -> 8561 bytes
-rw-r--r--doc/opcode.md214
-rw-r--r--examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb2
-rw-r--r--examples/mrbgems/c_and_ruby_extension_example/src/example.c6
-rw-r--r--examples/mrbgems/c_extension_example/src/example.c6
-rw-r--r--examples/mrbgems/ruby_extension_example/mrblib/example.rb2
-rw-r--r--examples/targets/build_config_android_arm64-v8a.rb26
-rw-r--r--examples/targets/build_config_android_armeabi.rb26
-rw-r--r--include/mrbconf.h183
-rw-r--r--include/mruby.h649
-rw-r--r--include/mruby/array.h77
-rw-r--r--include/mruby/boxing_nan.h138
-rw-r--r--include/mruby/boxing_no.h38
-rw-r--r--include/mruby/boxing_word.h269
-rw-r--r--include/mruby/class.h47
-rw-r--r--include/mruby/common.h33
-rw-r--r--include/mruby/compile.h48
-rw-r--r--include/mruby/data.h17
-rw-r--r--include/mruby/debug.h20
-rw-r--r--include/mruby/dump.h83
-rw-r--r--include/mruby/endian.h44
-rw-r--r--include/mruby/error.h92
-rw-r--r--include/mruby/gc.h7
-rw-r--r--include/mruby/hash.h79
-rw-r--r--include/mruby/irep.h113
-rw-r--r--include/mruby/istruct.h13
-rw-r--r--include/mruby/khash.h32
-rw-r--r--include/mruby/numeric.h159
-rw-r--r--include/mruby/object.h19
-rw-r--r--include/mruby/opcode.h8
-rw-r--r--include/mruby/ops.h87
-rw-r--r--include/mruby/presym.h40
-rw-r--r--include/mruby/presym/disable.h70
-rw-r--r--include/mruby/presym/enable.h37
-rw-r--r--include/mruby/presym/scanning.h73
-rw-r--r--include/mruby/proc.h142
-rw-r--r--include/mruby/range.h46
-rw-r--r--include/mruby/re.h4
-rw-r--r--include/mruby/string.h259
-rw-r--r--include/mruby/throw.h29
-rw-r--r--include/mruby/value.h311
-rw-r--r--include/mruby/variable.h29
-rw-r--r--include/mruby/version.h59
-rw-r--r--lib/mruby-core-ext.rb79
-rw-r--r--lib/mruby/build.rb353
-rw-r--r--lib/mruby/build/command.rb238
-rw-r--r--lib/mruby/build/load_gems.rb59
-rw-r--r--lib/mruby/core_ext.rb40
-rw-r--r--lib/mruby/gem.rb170
-rw-r--r--lib/mruby/lockfile.rb81
-rw-r--r--lib/mruby/presym.rb130
-rw-r--r--lib/mruby/source.rb4
-rwxr-xr-xminirake605
-rw-r--r--mrbgems/default-no-fpu.gembox3
-rw-r--r--mrbgems/default-no-stdio.gembox4
-rw-r--r--mrbgems/default.gembox87
-rw-r--r--mrbgems/full-core.gembox5
-rw-r--r--mrbgems/math.gembox10
-rw-r--r--mrbgems/metaprog.gembox15
-rw-r--r--mrbgems/mruby-array-ext/mrbgem.rake1
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb267
-rw-r--r--mrbgems/mruby-array-ext/src/array.c203
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb119
-rw-r--r--mrbgems/mruby-bin-config/mrbgem.rake35
-rw-r--r--mrbgems/mruby-bin-config/mruby-config34
-rw-r--r--mrbgems/mruby-bin-config/mruby-config.bat (renamed from mrbgems/mruby-bin-mruby-config/mruby-config.bat)12
-rw-r--r--mrbgems/mruby-bin-debugger/bintest/mrdb.rb9
-rw-r--r--mrbgems/mruby-bin-debugger/bintest/print.rb60
-rw-r--r--mrbgems/mruby-bin-debugger/mrbgem.rake2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c74
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c17
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c14
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apistring.c34
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apistring.h14
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c6
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c16
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c31
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c6
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c13
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h12
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h8
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h1
-rw-r--r--mrbgems/mruby-bin-mirb/bintest/mirb.rb28
-rw-r--r--mrbgems/mruby-bin-mirb/mrbgem.rake20
-rw-r--r--mrbgems/mruby-bin-mirb/tools/mirb/mirb.c124
-rw-r--r--mrbgems/mruby-bin-mrbc/bintest/mrbc.rb (renamed from mrbgems/mruby-compiler/bintest/mrbc.rb)0
-rw-r--r--mrbgems/mruby-bin-mrbc/mrbgem.rake7
-rw-r--r--mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c90
-rw-r--r--mrbgems/mruby-bin-mruby-config/mrbgem.rake31
-rw-r--r--mrbgems/mruby-bin-mruby-config/mruby-config20
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb110
-rw-r--r--mrbgems/mruby-bin-mruby/mrbgem.rake4
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c266
-rw-r--r--mrbgems/mruby-bin-strip/bintest/mruby-strip.rb2
-rw-r--r--mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c8
-rw-r--r--mrbgems/mruby-binding-core/mrbgem.rake7
-rw-r--r--mrbgems/mruby-binding-core/src/binding-core.c306
-rw-r--r--mrbgems/mruby-binding-core/test/binding-core.rb40
-rw-r--r--mrbgems/mruby-binding/mrbgem.rake11
-rw-r--r--mrbgems/mruby-binding/src/binding.c173
-rw-r--r--mrbgems/mruby-binding/test/binding.c13
-rw-r--r--mrbgems/mruby-binding/test/binding.rb77
-rw-r--r--mrbgems/mruby-catch/mrbgem.rake5
-rw-r--r--mrbgems/mruby-catch/mrblib/catch.rb8
-rw-r--r--mrbgems/mruby-catch/src/catch.c124
-rw-r--r--mrbgems/mruby-catch/test/catch.rb86
-rw-r--r--mrbgems/mruby-class-ext/mrblib/module.rb89
-rw-r--r--mrbgems/mruby-class-ext/src/class.c21
-rw-r--r--mrbgems/mruby-class-ext/test/module.rb54
-rw-r--r--mrbgems/mruby-cmath/mrbgem.rake8
-rw-r--r--mrbgems/mruby-cmath/src/cmath.c270
-rw-r--r--mrbgems/mruby-cmath/test/cmath.rb41
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c1217
-rw-r--r--mrbgems/mruby-compiler/core/keywords8
-rw-r--r--mrbgems/mruby-compiler/core/lex.def118
-rw-r--r--mrbgems/mruby-compiler/core/node.h1
-rw-r--r--mrbgems/mruby-compiler/core/parse.y1819
-rw-r--r--mrbgems/mruby-compiler/core/y.tab.c13969
-rw-r--r--mrbgems/mruby-compiler/mrbgem.rake43
-rw-r--r--mrbgems/mruby-complex/mrbgem.rake7
-rw-r--r--mrbgems/mruby-complex/mrblib/complex.rb76
-rw-r--r--mrbgems/mruby-complex/src/complex.c437
-rw-r--r--mrbgems/mruby-complex/test/complex.rb163
-rw-r--r--mrbgems/mruby-enum-chain/mrbgem.rake6
-rw-r--r--mrbgems/mruby-enum-chain/mrblib/chain.rb62
-rw-r--r--mrbgems/mruby-enum-chain/test/enum_chain.rb108
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb68
-rw-r--r--mrbgems/mruby-enum-ext/test/enum.rb24
-rw-r--r--mrbgems/mruby-enum-lazy/mrblib/lazy.rb2
-rw-r--r--mrbgems/mruby-enum-lazy/test/lazy.rb2
-rw-r--r--mrbgems/mruby-enumerator/mrbgem.rake1
-rw-r--r--mrbgems/mruby-enumerator/mrblib/enumerator.rb100
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb88
-rw-r--r--mrbgems/mruby-error/mrbgem.rake5
-rw-r--r--mrbgems/mruby-error/src/exception.c87
-rw-r--r--mrbgems/mruby-eval/src/eval.c373
-rw-r--r--mrbgems/mruby-eval/test/eval.rb35
-rw-r--r--mrbgems/mruby-exit/src/mruby-exit.c13
-rw-r--r--mrbgems/mruby-fiber/src/fiber.c64
-rw-r--r--mrbgems/mruby-fiber/test/fiber.rb2
-rw-r--r--mrbgems/mruby-hash-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-hash-ext/mrblib/hash.rb54
-rw-r--r--mrbgems/mruby-hash-ext/src/hash-ext.c36
-rw-r--r--mrbgems/mruby-hash-ext/test/hash.rb23
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.c17
-rw-r--r--mrbgems/mruby-io/.gitignore1
-rw-r--r--mrbgems/mruby-io/.travis.yml2
-rw-r--r--mrbgems/mruby-io/README.md48
-rw-r--r--mrbgems/mruby-io/include/mruby/ext/io.h52
-rw-r--r--mrbgems/mruby-io/mrbgem.rake12
-rw-r--r--mrbgems/mruby-io/mrblib/file.rb82
-rw-r--r--mrbgems/mruby-io/mrblib/file_constants.rb16
-rw-r--r--mrbgems/mruby-io/mrblib/io.rb117
-rw-r--r--mrbgems/mruby-io/mrblib/kernel.rb16
-rw-r--r--mrbgems/mruby-io/run_test.rb26
-rw-r--r--mrbgems/mruby-io/src/file.c255
-rw-r--r--mrbgems/mruby-io/src/file_test.c86
-rw-r--r--mrbgems/mruby-io/src/io.c719
-rw-r--r--mrbgems/mruby-io/test/file.rb110
-rw-r--r--mrbgems/mruby-io/test/file_test.rb21
-rw-r--r--mrbgems/mruby-io/test/gc_filedes.sh4
-rw-r--r--mrbgems/mruby-io/test/io.rb181
-rw-r--r--mrbgems/mruby-io/test/mruby_io_test.c151
-rw-r--r--mrbgems/mruby-kernel-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-kernel-ext/mrblib/kernel.rb15
-rw-r--r--mrbgems/mruby-kernel-ext/src/kernel.c160
-rw-r--r--mrbgems/mruby-kernel-ext/test/kernel.rb55
-rw-r--r--mrbgems/mruby-math/src/math.c43
-rw-r--r--mrbgems/mruby-math/test/math.rb229
-rw-r--r--mrbgems/mruby-metaprog/src/metaprog.c240
-rw-r--r--mrbgems/mruby-metaprog/test/metaprog.rb144
-rw-r--r--mrbgems/mruby-method/README.md56
-rw-r--r--mrbgems/mruby-method/mrblib/kernel.rb2
-rw-r--r--mrbgems/mruby-method/mrblib/method.rb12
-rw-r--r--mrbgems/mruby-method/mrblib/unbound_method.rb9
-rw-r--r--mrbgems/mruby-method/src/method.c394
-rw-r--r--mrbgems/mruby-method/test/method.rb44
-rw-r--r--mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb6
-rw-r--r--mrbgems/mruby-numeric-ext/src/numeric_ext.c60
-rw-r--r--mrbgems/mruby-numeric-ext/test/numeric.rb11
-rw-r--r--mrbgems/mruby-object-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-object-ext/mrblib/object.rb16
-rw-r--r--mrbgems/mruby-object-ext/src/object.c63
-rw-r--r--mrbgems/mruby-object-ext/test/nil.rb7
-rw-r--r--mrbgems/mruby-objectspace/src/mruby_objectspace.c42
-rw-r--r--mrbgems/mruby-objectspace/test/objectspace.rb4
-rw-r--r--mrbgems/mruby-os-memsize/mrbgem.rake10
-rw-r--r--mrbgems/mruby-os-memsize/src/memsize.c248
-rw-r--r--mrbgems/mruby-os-memsize/test/memsize.rb63
-rw-r--r--mrbgems/mruby-pack/.gitignore5
-rw-r--r--mrbgems/mruby-pack/.travis.yml2
-rw-r--r--mrbgems/mruby-pack/README.md93
-rw-r--r--mrbgems/mruby-pack/mrbgem.rake2
-rw-r--r--mrbgems/mruby-pack/packtest.rb157
-rw-r--r--mrbgems/mruby-pack/run_test.rb26
-rw-r--r--mrbgems/mruby-pack/src/pack.c652
-rw-r--r--mrbgems/mruby-pack/test/pack.rb132
-rw-r--r--mrbgems/mruby-print/mrblib/print.rb61
-rw-r--r--mrbgems/mruby-print/src/print.c53
-rw-r--r--mrbgems/mruby-proc-binding/mrbgem.rake9
-rw-r--r--mrbgems/mruby-proc-binding/src/proc-binding.c52
-rw-r--r--mrbgems/mruby-proc-binding/test/proc-binding.c14
-rw-r--r--mrbgems/mruby-proc-binding/test/proc-binding.rb22
-rw-r--r--mrbgems/mruby-proc-ext/mrblib/proc.rb8
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c93
-rw-r--r--mrbgems/mruby-proc-ext/test/proc.c2
-rw-r--r--mrbgems/mruby-proc-ext/test/proc.rb48
-rw-r--r--mrbgems/mruby-random/src/mt19937ar.c224
-rw-r--r--mrbgems/mruby-random/src/mt19937ar.h80
-rw-r--r--mrbgems/mruby-random/src/random.c377
-rw-r--r--mrbgems/mruby-random/src/random.h12
-rw-r--r--mrbgems/mruby-random/test/random.rb103
-rw-r--r--mrbgems/mruby-range-ext/mrblib/range.rb76
-rw-r--r--mrbgems/mruby-range-ext/src/range.c112
-rw-r--r--mrbgems/mruby-range-ext/test/range.rb136
-rw-r--r--mrbgems/mruby-rational/mrbgem.rake7
-rw-r--r--mrbgems/mruby-rational/mrblib/rational.rb15
-rw-r--r--mrbgems/mruby-rational/src/rational.c740
-rw-r--r--mrbgems/mruby-rational/test/rational.rb333
-rw-r--r--mrbgems/mruby-sleep/.gitignore4
-rw-r--r--mrbgems/mruby-sleep/.travis.yml29
-rw-r--r--mrbgems/mruby-sleep/.travis_build_config.rb6
-rw-r--r--mrbgems/mruby-sleep/README.md20
-rw-r--r--mrbgems/mruby-sleep/Rakefile29
-rw-r--r--mrbgems/mruby-sleep/example/sleep.rb5
-rw-r--r--mrbgems/mruby-sleep/mrbgem.rake4
-rw-r--r--mrbgems/mruby-sleep/src/sleep.c (renamed from mrbgems/mruby-sleep/src/mrb_sleep.c)17
-rw-r--r--mrbgems/mruby-sleep/test/sleep_test.rb41
-rw-r--r--mrbgems/mruby-socket/.travis.yml4
-rw-r--r--mrbgems/mruby-socket/README.md23
-rw-r--r--mrbgems/mruby-socket/mrbgem.rake4
-rw-r--r--mrbgems/mruby-socket/run_test.rb28
-rw-r--r--mrbgems/mruby-socket/src/socket.c190
-rw-r--r--mrbgems/mruby-socket/test/socket.rb2
-rw-r--r--mrbgems/mruby-socket/test/sockettest.c5
-rw-r--r--mrbgems/mruby-sprintf/src/kernel.c30
-rw-r--r--mrbgems/mruby-sprintf/src/sprintf.c356
-rw-r--r--mrbgems/mruby-sprintf/test/sprintf.rb41
-rw-r--r--mrbgems/mruby-string-ext/mrbgem.rake1
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb91
-rw-r--r--mrbgems/mruby-string-ext/src/string.c475
-rw-r--r--mrbgems/mruby-string-ext/test/numeric.rb29
-rw-r--r--mrbgems/mruby-string-ext/test/range.rb26
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb192
-rw-r--r--mrbgems/mruby-struct/mrblib/struct.rb140
-rw-r--r--mrbgems/mruby-struct/src/struct.c163
-rw-r--r--mrbgems/mruby-struct/test/struct.rb31
-rw-r--r--mrbgems/mruby-symbol-ext/src/symbol.c27
-rw-r--r--mrbgems/mruby-symbol-ext/test/symbol.rb28
-rw-r--r--mrbgems/mruby-test/README.md5
-rw-r--r--mrbgems/mruby-test/driver.c222
-rw-r--r--mrbgems/mruby-test/init_mrbtest.c39
-rw-r--r--mrbgems/mruby-test/mrbgem.rake83
-rw-r--r--mrbgems/mruby-test/vformat.c179
-rw-r--r--mrbgems/mruby-time/include/mruby/time.h26
-rw-r--r--mrbgems/mruby-time/src/time.c461
-rw-r--r--mrbgems/mruby-time/test/time.rb143
-rw-r--r--mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb2
-rw-r--r--mrbgems/mruby-toplevel-ext/test/toplevel.rb1
-rw-r--r--mrbgems/stdlib-ext.gembox18
-rw-r--r--mrbgems/stdlib-io.gembox12
-rw-r--r--mrbgems/stdlib.gembox54
-rw-r--r--mrblib/00class.rb12
-rw-r--r--mrblib/10error.rb8
-rw-r--r--mrblib/array.rb137
-rw-r--r--mrblib/enum.rb26
-rw-r--r--mrblib/float.rb9
-rw-r--r--mrblib/hash.rb73
-rw-r--r--mrblib/init_mrblib.c11
-rw-r--r--mrblib/kernel.rb2
-rw-r--r--mrblib/mrblib.rake21
-rw-r--r--mrblib/numeric.rb63
-rw-r--r--mrblib/range.rb52
-rw-r--r--mrblib/string.rb165
-rw-r--r--mruby-source.gemspec2
-rw-r--r--oss-fuzz/config/mruby.dict105
-rw-r--r--oss-fuzz/config/mruby_fuzzer.options5
-rw-r--r--oss-fuzz/config/mruby_proto_fuzzer.options4
-rw-r--r--oss-fuzz/mruby_fuzzer.c18
-rw-r--r--oss-fuzz/mruby_proto_fuzzer.cpp44
-rw-r--r--oss-fuzz/proto_to_ruby.cpp455
-rw-r--r--oss-fuzz/proto_to_ruby.h55
-rw-r--r--oss-fuzz/ruby.proto201
-rw-r--r--src/array.c302
-rw-r--r--src/backtrace.c205
-rw-r--r--src/cdump.c468
-rw-r--r--src/class.c1792
-rw-r--r--src/codedump.c284
-rw-r--r--src/crc.c39
-rw-r--r--src/debug.c214
-rw-r--r--src/dump.c542
-rw-r--r--src/enum.c22
-rw-r--r--src/error.c401
-rw-r--r--src/etc.c152
-rw-r--r--src/ext/.gitkeep0
-rw-r--r--src/fmt_fp.c644
-rw-r--r--src/gc.c533
-rw-r--r--src/hash.c1857
-rw-r--r--src/kernel.c431
-rw-r--r--src/load.c504
-rw-r--r--src/mruby_core.rake20
-rw-r--r--src/numeric.c1290
-rw-r--r--src/object.c315
-rw-r--r--src/pool.c2
-rw-r--r--src/print.c68
-rw-r--r--src/proc.c343
-rw-r--r--src/range.c477
-rw-r--r--src/readflt.c120
-rw-r--r--src/readint.c30
-rw-r--r--src/state.c204
-rw-r--r--src/string.c2195
-rw-r--r--src/symbol.c396
-rw-r--r--src/value_array.h1
-rw-r--r--src/variable.c465
-rw-r--r--src/vm.c1816
-rw-r--r--super-linter.report/.keep (renamed from bin/.gitkeep)0
-rw-r--r--tasks/benchmark.rake12
-rw-r--r--tasks/bin.rake23
-rw-r--r--tasks/core.rake12
-rw-r--r--tasks/doc.rake60
-rw-r--r--tasks/gitlab.rake118
-rw-r--r--tasks/libmruby.rake31
-rw-r--r--tasks/mrbgems.rake26
-rw-r--r--tasks/mrblib.rake40
-rw-r--r--tasks/presym.rake48
-rw-r--r--tasks/test.rake69
-rw-r--r--tasks/toolchains/android.rake51
-rw-r--r--tasks/toolchains/clang.rake7
-rw-r--r--tasks/toolchains/gcc.rake48
-rw-r--r--tasks/toolchains/openwrt.rake32
-rw-r--r--tasks/toolchains/visualcpp.rake58
-rw-r--r--test/assert.rb427
-rw-r--r--test/bintest.rb17
-rw-r--r--test/report.rb4
-rw-r--r--test/t/argumenterror.rb16
-rw-r--r--test/t/array.rb98
-rw-r--r--test/t/basicobject.rb1
-rw-r--r--test/t/bs_block.rb75
-rw-r--r--test/t/bs_literal.rb2
-rw-r--r--test/t/class.rb49
-rw-r--r--test/t/ensure.rb34
-rw-r--r--test/t/enumerable.rb6
-rw-r--r--test/t/exception.rb15
-rw-r--r--test/t/float.rb127
-rw-r--r--test/t/hash.rb1189
-rw-r--r--test/t/integer.rb128
-rw-r--r--test/t/iterations.rb2
-rw-r--r--test/t/kernel.rb105
-rw-r--r--test/t/literals.rb53
-rw-r--r--test/t/methods.rb29
-rw-r--r--test/t/module.rb146
-rw-r--r--test/t/nameerror.rb8
-rw-r--r--test/t/numeric.rb95
-rw-r--r--test/t/object.rb1
-rw-r--r--test/t/proc.rb2
-rw-r--r--test/t/range.rb105
-rw-r--r--test/t/string.rb366
-rw-r--r--test/t/superclass.rb23
-rw-r--r--test/t/symbol.rb8
-rw-r--r--test/t/syntax.rb92
-rw-r--r--test/t/unicode.rb14
-rw-r--r--test/t/vformat.rb58
-rw-r--r--travis_config.rb53
437 files changed, 47950 insertions, 21161 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..70cc78f0b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,43 @@
+# About this file, see:
+# Website: https://editorconfig.org/
+# For Emacs users: https://github.com/editorconfig/editorconfig-emacs
+# For Vim users: https://github.com/editorconfig/editorconfig-vim
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 8
+indent_style = tab
+insert_final_newline = true
+tab_width = 8
+
+[{Makefile,Makefile.*,makefile,*.mk}]
+trim_trailing_whitespace = true
+#max_line_length = 80
+
+[*.{c,cc,C,cxx,cpp,h,hh,H,hxx,hpp,inc,y}]
+indent_size = 2
+indent_style = space
+trim_trailing_whitespace = true
+#max_line_length = 120
+
+[{*.rb,Rakefile,rakefile,*.rake,*.gemspec,*.gembox}]
+indent_size = 2
+indent_style = space
+trim_trailing_whitespace = true
+#max_line_length = 120
+
+[*.bat]
+charset = latin1
+end_of_line = crlf
+#max_line_length = 80
+
+[*.{yaml,yml}]
+indent_size = 2
+indent_style = space
+
+[*.md]
+indent_size = 2
+indent_style = space
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 000000000..3dab080a9
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,4 @@
+# Extend the tab with 8 whitespace
+6fc5dc986a5ffc1a74c63ad0617ea1dcc10d3cdd
+# reformatted doc/opcode.md
+61e8a540869c57ebddac53cf9d243db407d57fff
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..dac4cac33
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+# Basic set up
+
+version: 2
+updates:
+
+ # Maintain dependencies for GitHub Actions
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/linters/.ecrc b/.github/linters/.ecrc
new file mode 100644
index 000000000..fb885d02a
--- /dev/null
+++ b/.github/linters/.ecrc
@@ -0,0 +1,12 @@
+{
+ "verbose": false,
+ "ignore_defaults": false,
+ "exclude": ["oss-fuzz/", "src/"],
+ "disable": {
+ "end_of_line": false,
+ "trim_trailing_whitespace": false,
+ "insert_final_newline": false,
+ "indent_size": true,
+ "indent_style": true
+ }
+}
diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml
new file mode 100644
index 000000000..a1023acbd
--- /dev/null
+++ b/.github/linters/.markdown-lint.yml
@@ -0,0 +1,26 @@
+# https://github.com/DavidAnson/markdownlint#rules--aliases
+# markdownlint -c .github/linters/.markdown-lint.yml .
+
+# MD001 heading-increment/header-increment - Heading levels should only increment by one level at a time
+MD001: false
+
+# MD010 no-hard-tabs - Hard tabs
+MD010: false
+
+# MD013 line-length - Line length
+MD013: false
+
+# MD014 commands-show-output - Dollar signs used before commands without showing output
+MD014: false
+
+# MD024 no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
+MD024: false
+
+# MD025 single-title/single-h1 - Multiple top-level headings in the same document
+MD025: false
+
+# MD026 no-trailing-punctuation - Trailing punctuation in heading
+MD026: false
+
+# MD040 fenced-code-language - Fenced code blocks should have a language specified
+MD040: false
diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml
new file mode 100644
index 000000000..be4c88798
--- /dev/null
+++ b/.github/linters/.yaml-lint.yml
@@ -0,0 +1,9 @@
+---
+# https://yamllint.readthedocs.io/en/stable/
+# yamllint --strict -c .github/linters/.yaml-lint.yml .
+extends: default
+
+rules:
+ document-start: disable
+ line-length: disable
+ truthy: false
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..6ee80eefd
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,155 @@
+name: Build & Test
+
+on: [push, pull_request]
+
+jobs:
+ Ubuntu-1804-gcc:
+ runs-on: ubuntu-18.04
+ timeout-minutes: 10
+ env:
+ MRUBY_CONFIG: ci/gcc-clang
+ CC: gcc
+ steps:
+ - uses: actions/[email protected]
+ - name: Ruby version
+ run: ruby -v
+ - name: Compiler version
+ run: ${{ env.CC }} --version
+ - name: Build and test
+ run: rake -m test:build && rake test:run
+
+ Ubuntu-1804-clang:
+ runs-on: ubuntu-18.04
+ timeout-minutes: 10
+ env:
+ MRUBY_CONFIG: ci/gcc-clang
+ CC: clang
+ steps:
+ - uses: actions/[email protected]
+ - name: Ruby version
+ run: ruby -v
+ - name: Compiler version
+ run: ${{ env.CC }} --version
+ - name: Build and test
+ run: rake -m test:build && rake test:run
+
+ Ubuntu-2004-gcc:
+ runs-on: ubuntu-20.04
+ timeout-minutes: 10
+ env:
+ MRUBY_CONFIG: ci/gcc-clang
+ CC: gcc
+ steps:
+ - uses: actions/[email protected]
+ - name: Ruby version
+ run: ruby -v
+ - name: Compiler version
+ run: ${{ env.CC }} --version
+ - name: Build and test
+ run: rake -m test:build && rake test:run
+
+ Ubuntu-2004-clang:
+ runs-on: ubuntu-20.04
+ timeout-minutes: 10
+ env:
+ MRUBY_CONFIG: ci/gcc-clang
+ CC: clang
+ steps:
+ - uses: actions/[email protected]
+ - name: Ruby version
+ run: ruby -v
+ - name: Compiler version
+ run: ${{ env.CC }} --version
+ - name: Build and test
+ run: rake -m test:build && rake test:run
+
+ macOS:
+ runs-on: macos-latest
+ timeout-minutes: 10
+ env:
+ MRUBY_CONFIG: ci/gcc-clang
+ CC: clang
+ steps:
+ - uses: actions/[email protected]
+ - name: Ruby version
+ run: ruby -v
+ - name: Compiler version
+ run: ${{ env.CC }} --version
+ - name: Build and test
+ run: rake -m test:build && rake test:run
+
+ Windows-MinGW:
+ runs-on: windows-latest
+ timeout-minutes: 10
+ env:
+ MRUBY_CONFIG: ci/gcc-clang
+ CC: gcc
+ steps:
+ - uses: actions/[email protected]
+ - name: Ruby version
+ run: ruby -v
+ - name: Compiler version
+ run: ${{ env.CC }} --version
+ - name: Build and test
+ run: rake -m test:build && rake test:run
+
+ Windows-Cygwin:
+ runs-on: windows-latest
+ timeout-minutes: 10
+ env:
+ ACTIONS_ALLOW_UNSECURE_COMMANDS: true
+ MRUBY_CONFIG: ci/gcc-clang
+ CC: gcc
+ installer-path: '%TMP%\cygwin-setup.exe'
+ cygwin-root: C:\cygwin
+ package-dir: C:\cygwin-package
+ cache-version: v1
+ steps:
+ - uses: actions/[email protected]
+ - uses: actions/[email protected]
+ with:
+ path: ${{ env.package-dir }}
+ key: ${{ runner.os }}-cygwin-${{ env.cache-version }}
+ - name: Download Cygwin installer
+ shell: cmd
+ run: >
+ bitsadmin /transfer download /priority foreground
+ https://cygwin.com/setup-x86_64.exe ${{ env.installer-path }}
+ - name: Install Cygwin
+ shell: cmd
+ run: >
+ ${{ env.installer-path }}
+ --quiet-mode --no-shortcuts --no-startmenu --no-desktop --no-admin
+ --only-site --site http://mirrors.kernel.org/sourceware/cygwin/
+ --root ${{ env.cygwin-root }}
+ --local-package-dir ${{ env.package-dir }}
+ --packages gcc-core,gcc-g++,ruby
+ - name: Set PATH for Cygwin
+ run: |
+ echo '::set-env name=PATH::${{ env.cygwin-root }}\bin;${{ env.cygwin-root }}\usr\bin'
+ - name: Ruby version
+ shell: cmd
+ run: ruby -v
+ - name: Compiler version
+ run: ${{ env.CC }} --version
+ - name: Build and test
+ shell: cmd
+ run: ruby /usr/bin/rake -m test:build && ruby /usr/bin/rake test:run
+ - name: Set PATH for cache archiving (tar)
+ # set Windows path so that Cygwin tar is not used for cache archiving
+ run: echo '::set-env name=PATH::C:\windows\System32'
+
+ Windows-VC:
+ runs-on: windows-latest
+ timeout-minutes: 10
+ env:
+ MRUBY_CONFIG: ci/msvc
+ steps:
+ - uses: actions/[email protected]
+ - name: Ruby version
+ run: ruby -v
+ - name: Build and test
+ shell: cmd
+ run: |
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ rake -m test:build && rake test:run
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000..4b0be0ed0
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,46 @@
+name: "Code scanning - action"
+
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '0 19 * * 4'
+
+jobs:
+ CodeQL-Build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/[email protected]
+ with:
+ # We must fetch at least the immediate parents so that if this is
+ # a pull request then we can checkout the head.
+ fetch-depth: 2
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ # Override language selection by uncommenting this and choosing your languages
+ # with:
+ # languages: go, javascript, csharp, python, cpp, java
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ # - run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 000000000..3e89c0328
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,32 @@
+name: Lint
+
+on: [pull_request]
+
+jobs:
+ misspell:
+ name: Check spelling with misspell
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check Out
+ uses: actions/[email protected]
+ - name: Install
+ run: wget -O - -q https://git.io/misspell | sh -s -- -b .
+ - name: Misspell
+ run: git ls-files --empty-directory | xargs ./misspell -error
+ pre-commit:
+ name: Run pre-commit
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/[email protected]
+ - name: Check merge conflict
+ run: |
+ python -m pip install --upgrade pip
+ pip install pre-commit
+ - name: Set PY
+ run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
+ - uses: actions/cache@v1
+ with:
+ path: ~/.cache/pre-commit
+ key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
+ - name: Run pre-commit
+ run: pre-commit run --all-files
diff --git a/.github/workflows/oss-fuzz.yml b/.github/workflows/oss-fuzz.yml
new file mode 100644
index 000000000..739e4afad
--- /dev/null
+++ b/.github/workflows/oss-fuzz.yml
@@ -0,0 +1,23 @@
+name: CIFuzz
+on: [pull_request]
+jobs:
+ Fuzzing:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'mruby'
+ dry-run: false
+ - name: Run Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'mruby'
+ fuzz-seconds: 600
+ dry-run: false
+ - name: Upload Crash
+ uses: actions/[email protected]
+ if: failure()
+ with:
+ name: artifacts
+ path: ./out/artifacts
diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml
new file mode 100644
index 000000000..5637475c7
--- /dev/null
+++ b/.github/workflows/super-linter.yml
@@ -0,0 +1,23 @@
+name: Super Linter
+
+on:
+ push:
+ branches-ignore: [master]
+ # Remove the line above to run when pushing to master
+ pull_request:
+ branches: [master]
+
+jobs:
+ build:
+ name: Lint Code Base
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/[email protected]
+ - uses: github/[email protected]
+ env:
+ ERROR_ON_MISSING_EXEC_BIT: true
+ VALIDATE_BASH: true
+ # VALIDATE_BASH_EXEC: true
+ # VALIDATE_EDITORCONFIG: true
+ # VALIDATE_SHELL_SHFMT: true
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 9c34b5f29..6a0e7e46b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,13 @@
-# /
+*.lock
*.bak
+*.bc
*.d
+*.i
*.o
-/benchmark/**/*.dat
-/benchmark/*.pdf
-/benchmark/*.png
*.orig
*.pdb
*.rej
+*.s
*.sav
*.swp
*.tmp
@@ -16,13 +16,22 @@
.ccmalloc
.svn
.vscode
-/.git
+.yardoc
+.ccls*
+compile_flags.txt
+compile_commands.json
cscope.files
cscope.out
tags
-/src/y.tab.c
+
+/.git
/bin
/build
/mruby-source-*.gem
-doc/api
-.yardoc
+
+/benchmark/**/*.dat
+/benchmark/*.pdf
+/benchmark/*.png
+
+/doc/api
+/doc/capi
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 8f4179232..000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,3214 +0,0 @@
----
-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
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 000000000..933324ae5
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,58 @@
+---
+default_stages: [commit, push]
+default_language_version:
+ # force all unspecified Python hooks to run python3
+ python: python3
+minimum_pre_commit_version: "1.20.0"
+repos:
+ - repo: meta
+ hooks:
+ - id: identity
+ - id: check-hooks-apply
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.0.1
+ hooks:
+ - id: check-added-large-files
+ - id: check-case-conflict
+ - id: check-executables-have-shebangs
+ exclude: ^test/t/lang\.rb$
+ - id: check-merge-conflict
+ - id: check-vcs-permalinks
+ - id: check-yaml
+ - id: detect-private-key
+ - id: end-of-file-fixer
+ - id: file-contents-sorter
+ files: ^codespell\.txt$
+ - id: fix-byte-order-marker
+ - id: mixed-line-ending
+ - id: trailing-whitespace
+ # - repo: git://github.com/Lucas-C/pre-commit-hooks
+ # rev: v1.1.9
+ # hooks:
+ # - id: forbid-tabs
+ # - id: remove-tabs
+ - repo: https://github.com/codespell-project/codespell
+ rev: v2.1.0
+ hooks:
+ - id: codespell
+ name: Run codespell
+ description: Check spelling with codespell
+ entry: codespell --ignore-words=codespell.txt
+ - repo: https://github.com/igorshubovych/markdownlint-cli
+ rev: v0.27.1
+ hooks:
+ - id: markdownlint
+ name: Run markdownlint
+ description: Checks the style of Markdown files
+ entry: markdownlint -c .github/linters/.markdown-lint.yml .
+ types: [markdown]
+ files: \.(md|mdown|markdown)$
+ - repo: https://github.com/adrienverge/yamllint
+ rev: v1.26.1
+ hooks:
+ - id: yamllint
+ name: Run yamllint
+ description: Check YAML files with yamllint
+ entry: yamllint --strict -c .github/linters/.yaml-lint.yml
+ types: [yaml]
+ files: \.(yaml|yml)$
diff --git a/.travis.yml b/.travis.yml
index 9ef7009a6..c47a54494 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,11 @@
language: c
-sudo: false
-
-matrix:
- include:
- - os: linux
- sudo: false
- - os: osx
- osx_image: xcode7.1
-
-addons:
- apt:
- packages:
- - gperf
+jobs:
+ - os: linux
+ - os: osx
env:
- - MRUBY_CONFIG=travis_config.rb
-script: "./minirake -j4 all test"
+ - MRUBY_CONFIG=ci/gcc-clang
+
+script:
+ - rake -m test:build && rake test:run
diff --git a/.yardopts b/.yardopts
index 27f6d59a1..4499bf513 100644
--- a/.yardopts
+++ b/.yardopts
@@ -12,6 +12,6 @@ mrbgems/*/mrblib/**/*.rb
mrbgems/*/include/**/*.h
-
AUTHORS
-MITL
+LICENSE
CONTRIBUTING.md
doc/guides/*.md
diff --git a/AUTHORS b/AUTHORS
index 4265f7f58..2a4b5f70e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,40 +1,305 @@
-Original Authors "mruby developers" are:
- Yukihiro Matsumoto
+# Authors of mruby (mruby developers)
+
+## The List of Contributors sorted by number of commits (as of 2021-07-01 99dbcec)
+
+ 7532 Yukihiro "Matz" Matsumoto (@matz)*
+ 585 KOBAYASHI Shuji (@shuujii)
+ 376 Daniel Bovensiepen (@bovi)
+ 344 Takeshi Watanabe (@take-cheeze)*
+ 334 Masaki Muranaka (@monaka)
+ 298 Tomoyuki Sahara (@tsahara)*
+ 294 dearblue (@dearblue)*
+ 234 Jun Hiroe (@suzukaze)
+ 217 Cremno (@cremno)*
+ 208 Yuki Kurihara (@ksss)+
+ 148 Yasuhiro Matsumoto (@mattn)*
+ 113 Carson McDonald (@carsonmcdonald)
+ 103 Tomasz Dąbrowski (@dabroz)*
+ 84 skandhas (@skandhas)
+ 80 Masamitsu MURASE (@masamitsu-murase)
+ 74 Yuichiro MASUI (@masuidrive)
+ 71 Tatsuhiko Kubo (@cubicdaiya)*
+ 67 Hiroshi Mimaki (@mimaki)*
+ 63 Akira Yumiyama (@akiray03)*
+ 62 Paolo Bosetti (@pbosetti)*
+ 59 Kurebayashi, Takahiro (@crimsonwoods)*
+ 56 h2so5 (@h2so5)
+ 52 Ralph Desir (@Mav7)*
+ 45 Rory O'Connell (@RoryO)*
+ 43 Yuichiro Kaneko (@yui-knk)+
+ 42 fleuria (@flaneur2020)
+ 40 Christopher Aue (@christopheraue)
+ 40 Seba Gamboa (@sagmor)
+ 39 John Bampton (@jbampton)
+ 38 Koji Yoshioka (@kyab)*+
+ 38 Kouhei Sutou (@kou)*
+ 31 MATSUMOTO Ryosuke (@matsumotory)*
+ 30 Nobuyoshi Nakada (@nobu)
+ 28 Masayoshi Takahashi (@takahashim)+
+ 24 Julian Aron Prenner (@furunkel)*
+ 24 Ryan Scott (@ryan-scott-dev)*
+ 22 Clayton Smith (@clayton-shopify)
+ 22 Zachary Scott (@zzak)*
+ 22 mirichi (@mirichi)
+ 21 Jared Breeden (@jbreeden)*
+ 19 Bouke van der Bijl (@bouk)
+ 19 Felix Jones (@felixjones)*
+ 19 Hidetaka Takano (@TJ-Hidetaka-Takano)
+ 19 Uchio Kondo (@udzura)*
+ 19 go kikuta (@gkta)*
+ 18 Corey Powell (@IceDragon200)
+ 18 Jon Maken (@jonforums)+
+ 18 Mitchell Blank Jr (@mitchblank)*
+ 17 Ryan Lopopolo (@lopopolo)
+ 16 bggd (@bggd)
+ 16 kano4 (@kano4)
+ 14 Blaž Hrastnik (@archseer)*
+ 14 Jose Narvaez (@goyox86)
+ 14 Kazuki Tsujimoto (@k-tsj)
+ 14 Tadashi FUKUZAWA (@FUKUZAWA-Tadashi)+
+ 13 Patrick Hogan (@pbhogan)
+ 12 Akira Kuroda (@akuroda)
+ 12 Kouki Ooyatsu (kaishuu0123)*
+ 12 NAKAMURA Usaku (@unak)*
+ 12 RIZAL Reckordp (@Reckordp)+
+ 12 Takashi Sawanaka (@sdottaka)*
+ 12 Ukrainskiy Sergey (@ukrainskiysergey)
+ 12 Xuejie "Rafael" Xiao (@xxuejie)*
+ 11 Julien Ammous (@schmurfy)
+ 11 Seeker (@SeekingMeaning)
+ 11 YAMAMOTO Masaya (pandax381)
+ 11 takkaw (@takkaw)
+ 10 Kazuho Oku (@kazuho)
+ 10 Miura Hideki (@miura1729)
+ 10 Narihiro Nakamura (@authorNari)
+ 10 Ray Chason (@chasonr)*
+ 10 Yuichi Nishiwaki (@nyuichi)
+ 9 Akira Mitsui (@murasesyuka)*
+ 9 Frank Celler (@fceller)
+ 9 Tatsuya Matsumoto (@tmash06)*
+ 8 Takashi Sogabe (@sogabe)
+ 7 Bhargava Shastry (@bshastry)*
+ 7 Kouichi Nakanishi (@keizo042)
+ 7 Rubyist (@expeditiousRubyist)
+ 7 Simon Génier (@simon-shopify)
+ 7 Terence Lee (@hone)
+ 7 Wataru Ashihara (@wataash)*
+ 7 roco (@rystyle)*
+ 6 Akito Mochizuki (@ak-mochi)
+ 6 Beoran (@beoran)
+ 6 David Siaw (@davidsiaw)*
+ 6 Frederick John Milens III (@fjmilens3)
+ 6 Hiro Asari (@BanzaiMan)
+ 6 INOUE Yasuyuki (@yasuyuki)
+ 6 Junji Sawada (@junjis0203)
+ 6 Kenji Okimoto (@okkez)+
+ 6 Selman ULUG (@selman)
+ 6 Yusuke Endoh (@mame)*
+ 5 Davide D'Agostino (@DAddYE)
+ 5 Eric Hodel (@drbrain)
+ 5 Hendrik (@Asmod4n)
+ 5 Ichito Nagata (@i110)
+ 5 Keita Obo (@ktaobo)*
+ 5 Max Anselm (@silverhammermba)
+ 5 SiZiOUS (@sizious)
+ 5 Syohei YOSHIDA (@syohex)
+ 5 TOMITA Masahiro (@tmtm)
+ 5 Yurie Yamane (@yurie)+
+ 5 dreamedge (@dreamedge)
+ 5 nkshigeru (@nkshigeru)
+ 4 Goro Kikuchi (@gorogit)
+ 4 Herwin Weststrate (@herwinw)
+ 4 Jon Moss (@maclover7)
+ 4 Ken Muramatsu (@ken-mu)+
+ 4 Kohei Suzuki (@eagletmt)
+ 4 Li Yazhou (@flaneur2020)
+ 4 Marcus Stollsteimer (@stomar)
+ 4 NARUSE, Yui (@nurse)
+ 4 Ravil Bayramgalin (@brainopia)*+
+ 4 Satoshi Odawara (@SatoshiOdawara)
+ 4 Yuhei Okazaki (@Yuuhei-Okazaki)*
+ 4 Yuji Yamano (@yyamano)
+ 4 kurodash (@kurodash)*
+ 4 masahino (@masahino)
+ 4 wanabe (@wanabe)*
+ 4 xuejianqing (@zhihuikeji)
+ 3 Anton Davydov (@davydovanton)
+ 3 Carlo Prelz (@asfluido)*
+ 3 David Turnbull (@AE9RB)
+ 3 Franck Verrot (@franckverrot)
+ 3 Horimoto Yasuhiro (@komainu8)
+ 3 J. Mutua (@katmutua)+
+ 3 Jan Berdajs (@mrbrdo)
+ 3 Joseph McCullough (@joequery)
+ 3 Mark McCurry (@fundamental)
+ 3 Nobuhiro Iwamatsu (@iwamatsu)
+ 3 Per Lundberg (@perlun)*
+ 3 Rob Fors (@robfors)*
+ 3 Rodrigo Malizia (@rmalizia44)+
+ 3 Sebastián Katzer (@katzer)*
+ 3 Shouji Kuboyama (@Shokuji)*
+ 3 Shuta Kimura (@kimushu)+
+ 3 TERAJIMA, Motoyuki (@trmmy)
+ 3 Takashi Kokubun (@k0kubun)
+ 3 Tatsuhiro Tsujikawa (@tatsuhiro-t)
+ 3 Thiago Scalone (@scalone)
+ 3 Vladimir Dementyev (@palkan)*
+ 3 William Light (@wrl)
+ 3 bamchoh (@bamchoh)
+ 3 sasaki takeru (@takeru)
+ 2 Akira Moroo (@retrage)
+ 2 Artur K (@nemerle)
+ 2 Christian Mauceri (@mauceri)
+ 2 Craig Lehmann (@craiglrock)*
+ 2 Dominic Sisneros (@dsisnero)*
+ 2 Dusan D. Majkic (@dmajkic)
+ 2 Emiliano Lesende (@3miliano)
+ 2 Francois Chagnon (@EiNSTeiN-)*
+ 2 Gilad Zohari (@gzohari)
+ 2 Go Saito (@govm)
+ 2 HASUMI Hitoshi (@hasumikin)
+ 2 Hirohito Higashi (@HirohitoHigashi)
+ 2 Hiroyuki Iwatsuki (@iwadon)
+ 2 Huei-Horng Yo (@hiroshiyui)
+ 2 Jonas Kulla (@Ancurio)
+ 2 Jun Takeda (@takjn)
+ 2 Kazuaki Tanaka (@kaz0505)
+ 2 Kazuhiko Yamashita (@pyama86)+
+ 2 Kazuhiro Sera (@seratch)
+ 2 Kuroda Daisuke (@dycoon)+
+ 2 Lanza (@LanzaSchneider)
+ 2 Lothar Scholz (@llothar)
+ 2 Lukas Joeressen (@kext)
+ 2 Masahiro Wakame (@vvkame)+
+ 2 Minao Yamamoto (@tarosay)+
+ 2 Nihad Abbasov (@NARKOZ)
+ 2 Robert Mosolgo (@rmosolgo)
+ 2 Russel Hunter Yukawa (@rhykw)+
+ 2 Ryunosuke SATO (@tricknotes)
+ 2 Santa Zhang (@santazhang)
+ 2 Satoru Naba (@snaba)+
+ 2 Serg Podtynnyi (@shtirlic)
+ 2 Shannen Saez (@shancat)
+ 2 SouthWolf (@southwolf)
+ 2 TJ Singleton (@tjsingleton)
+ 2 Taiyo Mizuhashi (@taiyoslime)+
+ 2 Tomás Pollak (@tomas)*
+ 2 Yutaka HARA (@yhara)*+
+ 2 Zhang Xiaohui (@hifoolno)
+ 2 icm7216 (@icm7216)
+ 2 windwiny (@windwiny)
+ 1 A-Sat (@asatou)+
+ 1 Abinoam Praxedes Marques Junior (@abinoam)
+ 1 Alex Wang (@nanamiwang)+
+ 1 Andrew Nordman (@cadwallion)
+ 1 Atsushi Morimoto (@mynz)
+ 1 Ben A Morgan (@BenMorganIO)
+ 1 Benoit Daloze (@eregon)
+ 1 Daehyub Kim (@lateau)
+ 1 Daniel Varga (@vargad)
+ 1 Edgar Boda-Majer (@eboda)
+ 1 Fangrui Song (@MaskRay)
+ 1 Flavio Medeiros (@flaviommedeiros)
+ 1 Francis Bogsanyi (@fbogsany)
+ 1 Guo Xiao (@guoxiao)
+ 1 Gwen Boatrite (@boatrit)
+ 1 HAYASHI Kentaro (@kenhys)
+ 1 Hiroki Mori (@yamori813)+
+ 1 Hiromasa Ishii (@Hir0)+
+ 1 Hiroyuki Matsuzaki (@Hiroyuki-Matsuzaki)
+ 1 Hugo Logmans (@hlogmans)
+ 1 Jack Danger Canty (@JackDanger)
+ 1 Jeff Federman (@jefffederman)
+ 1 Jeffrey Crowell (@crowell)
+ 1 Jeremy Ong (@jeremyong)
+ 1 Jiro Nishiguchi (@spiritloose)
+ 1 Joachim Baran (@indiedotkim)
+ 1 Joe Kutner (@jkutner)
+ 1 Jun Aruga (@junaruga)
+ 1 Junichi Kajiwara (@kjunichi)
+ 1 Jurriaan Pruis (@jurriaan)
+ 1 Katsuhiko Kageyama (@kishima)+
+ 1 Katsuyoshi Ito (@katsuyoshi)
+ 1 Kazuhiro NISHIYAMA (@znz)
+ 1 Kei Sawada (@remore)
+ 1 Kim H Madsen (@kimhmadsen)
+ 1 Konstantin Haase (@rkh)
+ 1 Leo Neat (@Leo-Neat)
+ 1 Lian Cheng (@liancheng)
+ 1 Luis Lavena (@luislavena)
+ 1 Lukas Elmer (@lukaselmer)
+ 1 Lukas Stabe (@Ahti)
+ 1 M.Naruoka (@fenrir-naru)
+ 1 Mark Delk (@jethrodaniel)
+ 1 Martin Bosslet (@emboss)+
+ 1 Masahiko Sawada (@MasahikoSawada)
+ 1 Matt Aimonetti (@mattetti)
+ 1 Maxim Abramchuk (@MaximAbramchuck)
+ 1 Megumi Tomita (@tomykaira)+
+ 1 Mitchell Hashimoto (@mitchellh)
+ 1 Mitsutaka Mimura (@takkanm)
+ 1 Nicholas (@knf)
+ 1 Nozomi SATO (@nozomiS)
+ 1 Okumura Takahiro (@hfm)
+ 1 Patrick Ellis (@pje)
+ 1 Patrick Pokatilo (@SHyx0rmZ)
+ 1 Pavel Evstigneev (@Paxa)+
+ 1 Prayag Verma (@pra85)
+ 1 Ranmocy (@ranmocy)
+ 1 Robert McNally (@wolfmcnally)
+ 1 Robert Rowe (@CaptainJet)
+ 1 Ryan Scott Lewis (@RyanScottLewis)
+ 1 Ryo Okubo (@syucream)
+ 1 Santiago Rodriguez (@sanrodari)
+ 1 Satoh, Hiroh (@cho45)+
+ 1 Sayed Abdelhaleem (@visualsayed)
+ 1 Shugo Maeda (@shugo)
+ 1 Sorah Fukumori (@sorah)
+ 1 Takuma Kume (@takumakume)+
+ 1 Thomas Schmidt (@digitaltom)
+ 1 Timo Schilling (@timoschilling)
+ 1 Tom Black (@blacktm)
+ 1 Utkarsh Kukreti (@utkarshkukreti)
+ 1 W (@graywolf)
+ 1 Wuffers Lightwolf (@w-x-l)
+ 1 Yevhen Viktorov (@yevgenko)
+ 1 Yoji SHIDARA (@darashi)
+ 1 Yoshiori SHOJI (@yoshiori)
+ 1 Yukang (@chenyukang)
+ 1 Yurii Nakonechnyi (@inobelar)
+ 1 Yusuke Suzuki (@Constellation)+
+ 1 Yusuke Tanaka (@csouls)
+ 1 arton (@arton)
+ 1 duangsuse (@duangsuse)
+ 1 fl0l0u (@fl0l0u)
+ 1 hhc0null (@hhc0null)
+ 1 iTitou (@titouanc)
+ 1 javier ramírez (@javier)
+ 1 liyuray (@liyuray)
+ 1 lucas dicioccio (@lucasdicioccio)
+ 1 n4o847 (@n4o847)
+ 1 robert (@R-obert)
+ 1 sbsoftware (@sbsoftware)
+ 1 ssmallkirby (@smallkirby)
+ 1 taku toyama (@tsuichu)
+
+`*` - Entries unified according to names and addresses
+`+` - Entries with names different from commits
+
+## Contributors without named commits
+
+ Yuichi Osawa (Mitsubishi Electric Micro-Computer Application Software)
+ Shota Nakano (Manycolors)
+ Bjorn De Meyer
+
+## Corporate contributors
+
+ Ministry of Economy, Trade and Industry, Japan
+ Kyushu Bureau of Economy, Trade and Industry
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
+ Specified non-profit organization mruby Forum
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
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 000000000..9f91caaf8
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1 @@
+* @matz
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6bababb89..516be1e3c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,7 @@
# How to contribute
mruby is an open-source project which is looking forward to each contribution.
+Contributors agree to license their contribution(s) under MIT license.
## Your Pull Request
@@ -9,7 +10,7 @@ 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```)
+* Test your changes before creating a pull request (`rake 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
@@ -17,6 +18,41 @@ things in mind before submitting your pull request:
* Use mrbgem to provide non ISO features (classes, modules and methods) unless
you have a special reason to implement them in the core
+## pre-commit
+
+A framework for managing and maintaining multi-language `pre-commit` hooks.
+`pre-commit` can be [installed](https://pre-commit.com/#installation) with `pip`, `curl`, `brew` or `conda`.
+
+You need to first install `pre-commit` and then install the `pre-commit` hooks with `pre-commit install`.
+Now `pre-commit` will run automatically on git commit!
+
+It's usually a good idea to run the hooks against all the files when adding new hooks (usually `pre-commit` will only run on the changed files during git hooks).
+Use `pre-commit run --all-files` to check all files.
+
+To run a single hook use `pre-commit run --all-files <hook_id>`
+
+To update use `pre-commit autoupdate`
+
+* [Quick start](https://pre-commit.com/#quick-start)
+* [Usage](https://pre-commit.com/#usage)
+* [pre-commit-autoupdate](https://pre-commit.com/#pre-commit-autoupdate)
+
+## Spell Checking
+
+We are running [misspell](https://github.com/client9/misspell) which is mainly written in
+[Golang](https://golang.org/) to check spelling with [GitHub Actions](.github/workflows/lint.yml).
+Correct commonly misspelled English words quickly with `misspell`. You can run `misspell` locally
+against all files with:
+
+```bash
+find . -type f | xargs ./misspell -error
+```
+
+Notable `misspell` help options or flags are:
+
+* `-i` string: ignore the following corrections, comma separated
+* `-w`: Overwrite file with corrections (default is just to display)
+
## Coding conventions
How to style your C and Ruby code which you want to submit.
@@ -31,11 +67,13 @@ C code:
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).
+(<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.
+Visual C++ is also an important target for mruby (supported version is 2013 or
+later). For this reason features that are not supported by Visual C++ may not
+be used (e.g. `%z` of `strftime()`).
+
+NOTE: Old GCC requires `-std=gnu99` option to enable C99 support.
#### Reduce library dependencies to a minimum
@@ -43,19 +81,15 @@ 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:
+#### Insert a break after the function return value:
- int
- main(void)
- {
- ...
- }
+```c
+int
+main(void)
+{
+ ...
+}
+```
### Ruby code
@@ -65,4 +99,5 @@ 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).
+30170:2012 (<https://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579>),
+unless there's a clear reason, e.g. the latest Ruby has changed behavior from ISO.
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 000000000..cb0bf5546
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,2408 @@
+# Doxyfile 1.8.13
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = "mruby"
+PROJECT_NUMBER = 3.0.0
+
+PROJECT_BRIEF = "mruby is the lightweight implementation of the Ruby language"
+
+PROJECT_LOGO = doc/mruby_logo_red_icon.png
+
+OUTPUT_DIRECTORY = doc/capi
+
+USE_MDFILE_AS_MAINPAGE = README.md
+
+INPUT = README.md \
+ src \
+ include \
+ include/mruby \
+ mrblib \
+ doc \
+ doc/guides
+
+# Red for Ruby
+HTML_COLORSTYLE_HUE = 359
+
+# The following expansions
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = NO
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = NO
+
+# This tells doxygen to search the places that make sense
+SEARCH_INCLUDES = YES
+INCLUDE_PATH = include include/mruby
+INCLUDE_FILE_PATTERNS = *.h
+
+CLANG_ASSISTED_PARSING = NO
+CLANG_OPTIONS = -I./include
+
+# This thing creates documentation elements for everything, even when its not documented. Its a little ugly to do it right now because huge swathes of code aren't documented.
+EXTRACT_ALL = NO
+
+# Document MRB_INLINE functions
+EXTRACT_STATIC = YES
+
+JAVADOC_AUTOBRIEF = YES
+QT_AUTOBRIEF = NO
+
+QUIET = YES
+WARN_IF_UNDOCUMENTED = NO
+
+#===========================================================================
+# BELOW THIS LINE IS CRUFT GENERATED BY doxygen -g
+# If you edit anything below this, bring it up here so its easier to read.
+#===========================================================================
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/LEGAL b/LEGAL
index 84929998c..ecddf6b95 100644
--- a/LEGAL
+++ b/LEGAL
@@ -2,5 +2,4 @@ LEGAL NOTICE INFORMATION
------------------------
All the files in this distribution are covered under the MIT license
-(see the file MITL) except some files mentioned below:
-
+(see the file LICENSE) except some files mentioned below:
diff --git a/MITL b/LICENSE
index 8d8c78fab..985dd3665 100644
--- a/MITL
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2018 mruby developers
+Copyright (c) 2010-2021 mruby developers
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -17,4 +17,3 @@ 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.
-
diff --git a/Makefile b/Makefile
index 4912f17e1..6e20d9dbf 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,6 @@
-# mruby is using Rake (http://rake.rubyforge.org) as a build tool.
-# We provide a minimalistic version called minirake inside of our
-# codebase.
+# mruby is using Rake (https://ruby.github.io/rake/) as a build tool.
-RAKE = ruby ./minirake
+RAKE = rake
all :
$(RAKE)
diff --git a/NEWS b/NEWS
index 5c2523826..8e8c1e41a 100644
--- a/NEWS
+++ b/NEWS
@@ -9,5 +9,3 @@ with all sufficient information, see the ChangeLog file.
** Information about first release v1.0.0
-
-
diff --git a/README.md b/README.md
index 529091bdc..548806add 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,15 @@
+# mruby
+
[![Build Status][build-status-img]][travis-ci]
+[![GitHub Super-Linter](https://github.com/mruby/mruby/workflows/Lint%20Code%20Base/badge.svg)](https://github.com/marketplace/actions/super-linter)
## 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.
+of) the [ISO standard][ISO-standard]. Its syntax is Ruby 2.x compatible.
mruby can be linked and embedded within your application. We provide the
-interpreter program "mruby" and the interactive mruby shell "mirb" as examples.
+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"
@@ -17,49 +20,58 @@ of the Ministry of Economy, Trade and Industry of Japan.
## How to get mruby
-The stable version 1.4.1 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.4.1.zip](https://github.com/mruby/mruby/archive/1.4.1.zip)
+The stable version 3.0.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/3.0.0.zip](https://github.com/mruby/mruby/archive/3.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
+```
+$ 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).
+The URL of the mruby home-page is: <https://mruby.org>.
## Mailing list
-We don't have a mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby).
+We don't have a mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby/issues).
+
+## How to compile, test, and install (mruby and gems)
-## How to compile and install (mruby and gems)
+See the [compile.md](doc/guides/compile.md) file.
-See the [doc/guides/compile.md](doc/guides/compile.md) file.
+## Building documentation
-## Running Tests
+There are two sets of documentation in mruby: the mruby API (generated by yard) and C API (Doxygen)
-To run the tests, execute the following from the project's root directory.
+To build both of them, simply go
- $ make test
+```
+rake doc
+```
-Or
+You can also view them in your browser
- $ ruby ./minirake test
+```
+rake view_api
+rake view_capi
+```
## 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/*.
+use mrbgems consult the file [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).
+mruby is released under the [MIT License](LICENSE).
## Note for License
@@ -73,7 +85,7 @@ 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
+Actual copyright holder name (contributors) will be listed in the [AUTHORS](AUTHORS)
file.)
Please ask us if you want to distribute your code under another license.
@@ -81,12 +93,10 @@ 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 <http://github.com/mruby/mruby>. 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.
+request to <https://github.com/mruby/mruby>. We consider you have granted
+non-exclusive right to your contributed code under MIT license.
-[ISO-standard]: http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579
+[ISO-standard]: https://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
+[contribution-guidelines]: https://github.com/mruby/mruby/blob/master/CONTRIBUTING.md
[travis-ci]: https://travis-ci.org/mruby/mruby
diff --git a/Rakefile b/Rakefile
index d2ad9044c..227d1d5d5 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,15 +5,16 @@ 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')
+Rake.verbose(false) if Rake.verbose == Rake::DSL::DEFAULT
+
$LOAD_PATH << File.join(MRUBY_ROOT, "lib")
# load build systems
-require "mruby-core-ext"
+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"
+MRUBY_CONFIG = MRuby::Build.mruby_config_path
load MRUBY_CONFIG
# load basic rules
@@ -22,142 +23,47 @@ MRuby.each_target do |build|
end
# load custom rules
-load "#{MRUBY_ROOT}/src/mruby_core.rake"
-load "#{MRUBY_ROOT}/mrblib/mrblib.rake"
-
+load "#{MRUBY_ROOT}/tasks/core.rake"
+load "#{MRUBY_ROOT}/tasks/mrblib.rake"
load "#{MRUBY_ROOT}/tasks/mrbgems.rake"
load "#{MRUBY_ROOT}/tasks/libmruby.rake"
-
+load "#{MRUBY_ROOT}/tasks/bin.rake"
+load "#{MRUBY_ROOT}/tasks/presym.rake"
+load "#{MRUBY_ROOT}/tasks/test.rake"
load "#{MRUBY_ROOT}/tasks/benchmark.rake"
-
-load "#{MRUBY_ROOT}/tasks/gitlab.rake"
+load "#{MRUBY_ROOT}/tasks/doc.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 + [libfile("#{build_dir}/lib/libmruby")] 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.libfile("#{t.build_dir}/lib/libmruby")]
-}.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
+task :all => :gensym do
+ Rake::Task[:build].invoke
puts
puts "Build summary:"
puts
- MRuby.each_target do
- print_build_summary
+ MRuby.each_target do |build|
+ build.print_build_summary
end
+ MRuby::Lockfile.write
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
+task :build => MRuby.targets.flat_map{|_, build| build.products}
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 }
+ MRuby.each_target do |build|
+ rm_rf build.build_dir
+ rm_f build.products
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 }
+task :deep_clean => %w[clean doc:clean] do
+ MRuby.each_target do |build|
+ rm_rf build.gem_clone_dir
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
diff --git a/TODO b/TODO
deleted file mode 100644
index 3e195f99b..000000000
--- a/TODO
+++ /dev/null
@@ -1,10 +0,0 @@
-Things 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.
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 000000000..2980547c9
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,15 @@
+# Thing to Do in the future
+
+# After mruby 3.0
+
+* multi-precision integer
+* WORD_BOXING: Pack some floats in `mrb_value`
+* NAN_BOXING: Allow `MRB_INT64` along with NaN boxing
+* keyword arguments à la Ruby3.0 (update `OP_SEND`)
+* parser and code generator independent from `mrb_state` (picoruby?)
+
+# Things to do (Things that are not done yet)
+
+* `begin ... end while cond` to behave as CRuby
+* special variables ($1,$2..)
+* super in aliased methods
diff --git a/appveyor.yml b/appveyor.yml
index b4514ec27..8782f59aa 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,38 +1,45 @@
version: "{build}"
-os: Visual Studio 2015
+shallow_clone: true
-clone_depth: 50
+environment:
+ matrix:
+ - job_name: Visual Studio 2019 64bit
+ visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat
+ appveyor_build_worker_image: Visual Studio 2019
+ - job_name: Visual Studio 2019 32bit
+ visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat
+ appveyor_build_worker_image: Visual Studio 2019
-cache:
- - win_flex_bison-2.5.10.zip
+ - job_name: Visual Studio 2017 64bit
+ visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat
+ appveyor_build_worker_image: Visual Studio 2017
+ - job_name: Visual Studio 2017 32bit
+ visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat
+ appveyor_build_worker_image: Visual Studio 2017
-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
+ - job_name: Visual Studio 2015 64bit
+ visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
+ appveyor_build_worker_image: Visual Studio 2015
+ machine: x86_amd64
+ - job_name: Visual Studio 2015 32bit
+ visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
+ appveyor_build_worker_image: Visual Studio 2015
+ machine: x86
init:
- call "%visualcpp%" %machine%
- # For using Rubyinstaller's Ruby 2.4 64bit
- - set PATH=C:\Ruby24-x64\bin;%PATH%
+ # For using RubyInstaller's Ruby 2.6 64bit
+ # 2.6 is the highest supported Ruby version across all historical
+ # Visual Studio AppVeyor images. Ruby 2.7 is only on the 2019 image.
+ - set PATH=C:\Ruby26-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
+ - set MRUBY_CONFIG=ci/msvc
+ - rake -m test:build
+ - rake test:run
diff --git a/benchmark/bm_ao_render.rb b/benchmark/bm_ao_render.rb
index dc4388c7d..e55c34e09 100644
--- a/benchmark/bm_ao_render.rb
+++ b/benchmark/bm_ao_render.rb
@@ -1,5 +1,5 @@
# AO render benchmark
-# Original program (C) Syoyo Fujita in Javascript (and other languages)
+# 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
@@ -260,7 +260,7 @@ class Scene
w.times do |x|
rad = Vec.new(0.0, 0.0, 0.0)
- # Subsmpling
+ # Subsampling
nsubsamples.times do |v|
nsubsamples.times do |u|
cnt = cnt + 1
diff --git a/benchmark/bm_app_lc_fizzbuzz.rb b/benchmark/bm_app_lc_fizzbuzz.rb
index 26283cc3f..de8268577 100644
--- a/benchmark/bm_app_lc_fizzbuzz.rb
+++ b/benchmark/bm_app_lc_fizzbuzz.rb
@@ -48,5 +48,4 @@ answer = to_array(solution).map do |p|
to_string(p)
end
-answer_str = answer.to_a
-# puts answer_str
+# puts answer
diff --git a/benchmark/bm_so_lists.rb b/benchmark/bm_so_lists.rb
index 512e312cc..fdee0e4d4 100644
--- a/benchmark/bm_so_lists.rb
+++ b/benchmark/bm_so_lists.rb
@@ -1,4 +1,4 @@
-#from http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby
+# from http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby
NUM = 300
SIZE = 10000
diff --git a/benchmark/build_config_boxing.rb b/benchmark/build_config_boxing.rb
deleted file mode 100644
index b478c9005..000000000
--- a/benchmark/build_config_boxing.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-MRuby::Build.new do |conf|
- toolchain :gcc
-end
-
-MRuby::Build.new('no_boxing') do |conf|
- toolchain :gcc
-
- conf.gembox 'default'
-end
-
-MRuby::Build.new('word_boxing') do |conf|
- toolchain :gcc
-
- conf.gembox 'default'
- conf.compilers.each do |c|
- c.defines += %w(MRB_WORD_BOXING)
- end
-end
-
-MRuby::Build.new('nan_boxing') do |conf|
- toolchain :gcc
-
- conf.gembox 'default'
- conf.compilers.each do |c|
- c.defines += %w(MRB_NAN_BOXING)
- end
-end
-
diff --git a/benchmark/build_config_cc.rb b/benchmark/build_config_cc.rb
deleted file mode 100644
index 56d725bc7..000000000
--- a/benchmark/build_config_cc.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-MRuby::Build.new do |conf|
- toolchain :gcc
-end
-
-MRuby::Build.new('gcc') do |conf|
- toolchain :gcc
- conf.gembox 'default'
-end
-
-MRuby::Build.new('clang') do |conf|
- toolchain :clang
- conf.gembox 'default'
-end
diff --git a/build_config.rb b/build_config.rb
index 7f52b5ee9..625661df0 100644
--- a/build_config.rb
+++ b/build_config.rb
@@ -1,152 +1,9 @@
-MRuby::Build.new do |conf|
- # load specific toolchain settings
-
- # Gets set by the VS command prompts.
- if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
- toolchain :visualcpp
- else
- toolchain :gcc
- end
-
- # Turn on `enable_debug` for better debugging
- # enable_debug
-
- # Use mrbgems
- # conf.gem 'examples/mrbgems/ruby_extension_example'
- # conf.gem 'examples/mrbgems/c_extension_example' do |g|
- # g.cc.flags << '-g' # append cflags in this gem
- # end
- # conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
- # conf.gem :core => 'mruby-eval'
- # conf.gem :mgem => 'mruby-io'
- # conf.gem :github => 'iij/mruby-io'
- # conf.gem :git => '[email protected]: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
+# The default build configuration file was moved to `build_config/default.rb`.
+#
+# Recommended way to customize the build configuration is:
+# * copy `default.rb` (or any config file) to a new file (e.g. `myconfig.rb`)
+# * edit `myconfig.rb`.
+# * `rake MRUBY_CONFIG=/path/to/myconfig.rb` to compile and test.
+# * or `rake MRUBY_CONFIG=myconfig` if your configuration file is in the `build_config` directory.
+# * (optional) submit your configuration as a pull-request if it's useful for others
+raise "The default configuration was moved to `build_config/default.rb`"
diff --git a/examples/targets/build_config_ArduinoDue.rb b/build_config/ArduinoDue.rb
index 527aaa4f6..9baf1ef92 100644
--- a/examples/targets/build_config_ArduinoDue.rb
+++ b/build_config/ArduinoDue.rb
@@ -1,21 +1,5 @@
-MRuby::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
+# https://arduino.cc/en/Main/ArduinoBoardDue
#
# Requires Arduino IDE >= 1.5
MRuby::CrossBuild.new("ArduinoDue") do |conf|
@@ -27,7 +11,7 @@ MRuby::CrossBuild.new("ArduinoDue") do |conf|
# ARDUINO_PATH = '/Applications/Arduino.app/Contents/Java'
# GNU Linux
ARDUINO_PATH = '/opt/arduino'
- # Arduino IDE <= 1.5.6
+ # 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"
@@ -42,14 +26,13 @@ MRuby::CrossBuild.new("ArduinoDue") do |conf|
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}"
+ cc.compile_options = %Q[%{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(MRB_NO_STDIO) #if you don't need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
end
@@ -64,7 +47,7 @@ MRuby::CrossBuild.new("ArduinoDue") do |conf|
conf.archiver do |archiver|
archiver.command = "#{BIN_PATH}/arm-none-eabi-ar"
- archiver.archive_options = 'rcs %{outfile} %{objs}'
+ archiver.archive_options = 'rcs "%{outfile}" %{objs}'
end
#no executables
diff --git a/examples/targets/build_config_IntelEdison.rb b/build_config/IntelEdison.rb
index 8fa3aa0c0..65b6a9a5f 100644
--- a/examples/targets/build_config_IntelEdison.rb
+++ b/build_config/IntelEdison.rb
@@ -5,7 +5,7 @@
MRuby::Build.new do |conf|
toolchain :gcc
conf.gembox 'default'
- conf.cc.defines = %w(ENABLE_READLINE)
+ conf.cc.defines = %w(MRB_USE_READLINE)
conf.gembox 'default'
#lightweight regular expression
@@ -21,7 +21,7 @@ MRuby::CrossBuild.new('core2-32-poky-linux') do |conf|
#
POKY_EDISON_PATH = '/opt/poky-edison/1.7.2'
- POKY_EDISON_SYSROOT = "#{POKY_EDISON_PATH}/sysroots/core2-32-poky-linux"
+ 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"
@@ -32,8 +32,8 @@ MRuby::CrossBuild.new('core2-32-poky-linux') do |conf|
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)
+ cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"]
+ cc.defines = %w(MRB_USE_READLINE)
end
conf.cxx do |cxx|
@@ -47,7 +47,7 @@ MRuby::CrossBuild.new('core2-32-poky-linux') do |conf|
conf.archiver do |archiver|
archiver.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-ar"
- archiver.archive_options = 'rcs %{outfile} %{objs}'
+ archiver.archive_options = 'rcs "%{outfile}" %{objs}'
end
conf.linker do |linker|
diff --git a/examples/targets/build_config_IntelGalileo.rb b/build_config/IntelGalileo.rb
index 42f800d9f..f1f39e5dc 100644
--- a/examples/targets/build_config_IntelGalileo.rb
+++ b/build_config/IntelGalileo.rb
@@ -1,22 +1,5 @@
-MRuby::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
+# https://arduino.cc/en/ArduinoCertified/IntelGalileo
#
# Requires Arduino IDE for Intel Galileo
MRuby::CrossBuild.new("Galileo") do |conf|
@@ -39,7 +22,7 @@ MRuby::CrossBuild.new("Galileo") do |conf|
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}"
+ cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"]
end
conf.cxx do |cxx|
@@ -54,7 +37,7 @@ MRuby::CrossBuild.new("Galileo") do |conf|
conf.archiver do |archiver|
archiver.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-ar"
- archiver.archive_options = 'rcs %{outfile} %{objs}'
+ archiver.archive_options = 'rcs "%{outfile}" %{objs}'
end
conf.linker do |linker|
@@ -91,7 +74,7 @@ MRuby::CrossBuild.new("Galileo") do |conf|
conf.gem :core => "mruby-fiber"
conf.gem :core => "mruby-toplevel-ext"
- #lightweigh regular expression
+ #lightweight regular expression
conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master"
#Arduino API
diff --git a/examples/targets/build_config_RX630.rb b/build_config/RX630.rb
index fd17eae99..7c3cfc792 100644
--- a/examples/targets/build_config_RX630.rb
+++ b/build_config/RX630.rb
@@ -1,19 +1,3 @@
-MRuby::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/
#
@@ -27,15 +11,14 @@ MRuby::CrossBuild.new("RX630") do |conf|
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}"
+ cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"]
#configuration for low memory environment
- cc.defines << %w(MRB_USE_FLOAT)
+ cc.defines << %w(MRB_USE_FLOAT32)
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(MRB_NO_STDIO) #if you don't need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
end
@@ -53,7 +36,7 @@ MRuby::CrossBuild.new("RX630") do |conf|
conf.archiver do |archiver|
archiver.command = "#{BIN_PATH}/rx-elf-ar"
- archiver.archive_options = 'rcs %{outfile} %{objs}'
+ archiver.archive_options = 'rcs "%{outfile}" %{objs}'
end
#no executables
diff --git a/build_config/android_arm64_v8a.rb b/build_config/android_arm64_v8a.rb
new file mode 100644
index 000000000..7fab2f063
--- /dev/null
+++ b/build_config/android_arm64_v8a.rb
@@ -0,0 +1,11 @@
+# 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
diff --git a/build_config/android_armeabi.rb b/build_config/android_armeabi.rb
new file mode 100644
index 000000000..41a657c3b
--- /dev/null
+++ b/build_config/android_armeabi.rb
@@ -0,0 +1,11 @@
+# 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
diff --git a/examples/targets/build_config_android_armeabi_v7a_neon_hard.rb b/build_config/android_armeabi_v7a_neon_hard.rb
index 3788bba7f..6129b238e 100644
--- a/examples/targets/build_config_android_armeabi_v7a_neon_hard.rb
+++ b/build_config/android_armeabi_v7a_neon_hard.rb
@@ -1,18 +1,3 @@
-MRuby::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 = {
diff --git a/build_config/bench.rb b/build_config/bench.rb
new file mode 100644
index 000000000..30ec56ad3
--- /dev/null
+++ b/build_config/bench.rb
@@ -0,0 +1,11 @@
+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
diff --git a/build_config/boxing.rb b/build_config/boxing.rb
new file mode 100644
index 000000000..4b07c22e1
--- /dev/null
+++ b/build_config/boxing.rb
@@ -0,0 +1,21 @@
+boxings = %w[no word nan]
+bits = [64, 32]
+ints = [64, 32]
+
+boxings.product(bits, ints) do |boxing, bit, int|
+ next if boxing == "nan" && int == 64
+
+ MRuby::Build.new("boxing-#{boxing}-m#{bit}-i#{int}") do |conf|
+ conf.toolchain :gcc
+ conf.gembox 'default'
+ conf.compilers.each do |c|
+ c.defines << "MRB_#{boxing.upcase}_BOXING"
+ c.defines << "MRB_INT#{int}"
+ c.flags << "-m#{bit}"
+ end
+ conf.linker.flags << "-m#{bit}"
+ conf.enable_debug
+ conf.enable_test
+ conf.enable_bintest
+ end
+end
diff --git a/examples/targets/build_config_chipKITMax32.rb b/build_config/chipKITMax32.rb
index 951f71483..cb3192610 100644
--- a/examples/targets/build_config_chipKITMax32.rb
+++ b/build_config/chipKITMax32.rb
@@ -1,21 +1,5 @@
-MRuby::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
+# https://www.digilentinc.com/Products/Detail.cfm?Prod=CHIPKIT-MAX32
#
# Requires MPIDE (https://github.com/chipKIT32/chipKIT32-MAX)
#
@@ -39,14 +23,13 @@ MRuby::CrossBuild.new("chipKITMax32") do |conf|
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}"
+ cc.compile_options = %Q[%{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(MRB_NO_STDIO) #if you don't need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
end
@@ -60,7 +43,7 @@ MRuby::CrossBuild.new("chipKITMax32") do |conf|
conf.archiver do |archiver|
archiver.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-ar"
- archiver.archive_options = 'rcs %{outfile} %{objs}'
+ archiver.archive_options = 'rcs "%{outfile}" %{objs}'
end
#no executables
diff --git a/appveyor_config.rb b/build_config/ci/gcc-clang.rb
index 2555b2f62..aa4a1a87d 100644
--- a/appveyor_config.rb
+++ b/build_config/ci/gcc-clang.rb
@@ -1,32 +1,22 @@
-MRuby::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
+STDOUT.sync = STDERR.sync = true unless Rake.application.options.always_multitask
MRuby::Build.new('full-debug') do |conf|
- toolchain :visualcpp
- enable_debug
+ conf.toolchain
+ conf.enable_debug
# include all core GEMs
conf.gembox 'full-core'
- conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
+ conf.cc.defines += %w(MRB_GC_STRESS MRB_USE_DEBUG_HOOK)
conf.enable_test
end
MRuby::Build.new do |conf|
- toolchain :visualcpp
+ conf.toolchain
# include all core GEMs
conf.gembox 'full-core'
+ conf.gem :core => 'mruby-bin-debugger'
conf.compilers.each do |c|
c.defines += %w(MRB_GC_FIXED_ARENA)
end
@@ -35,16 +25,16 @@ MRuby::Build.new do |conf|
end
MRuby::Build.new('cxx_abi') do |conf|
- toolchain :visualcpp
+ conf.toolchain
conf.gembox 'full-core'
+ conf.cc.flags += %w(-fpermissive)
conf.compilers.each do |c|
c.defines += %w(MRB_GC_FIXED_ARENA)
end
- conf.enable_bintest
conf.enable_test
- enable_cxx_abi
+ conf.enable_cxx_abi
- build_mrbc_exec
+ conf.build_mrbc_exec
end
diff --git a/build_config/ci/msvc.rb b/build_config/ci/msvc.rb
new file mode 100644
index 000000000..312af78ec
--- /dev/null
+++ b/build_config/ci/msvc.rb
@@ -0,0 +1,20 @@
+STDOUT.sync = STDERR.sync = true unless Rake.application.options.always_multitask
+
+def setup_option(conf)
+ conf.cc.compile_options.sub!(%r{/Zi }, "") unless ENV['CFLAGS']
+ conf.cxx.compile_options.sub!(%r{/Zi }, "") unless ENV['CFLAGS'] || ENV['CXXFLAGS']
+ conf.linker.flags << "/DEBUG:NONE" unless ENV['LDFLAGS']
+end
+
+MRuby::Build.new do |conf|
+ conf.toolchain :visualcpp
+
+ # include all core GEMs
+ conf.gembox 'full-core'
+ conf.compilers.each do |c|
+ c.defines += %w(MRB_GC_FIXED_ARENA)
+ end
+ setup_option(conf)
+ conf.enable_bintest
+ conf.enable_test
+end
diff --git a/build_config/clang-asan.rb b/build_config/clang-asan.rb
new file mode 100644
index 000000000..19cc53bf0
--- /dev/null
+++ b/build_config/clang-asan.rb
@@ -0,0 +1,11 @@
+MRuby::Build.new do |conf|
+ conf.toolchain :clang
+ # include the GEM box
+ conf.gembox 'full-core'
+
+ # Turn on `enable_debug` for better debugging
+ conf.enable_sanitizer "address,undefined"
+ conf.enable_debug
+ conf.enable_bintest
+ conf.enable_test
+end
diff --git a/build_config/cross-32bit.rb b/build_config/cross-32bit.rb
new file mode 100644
index 000000000..b3feb21f6
--- /dev/null
+++ b/build_config/cross-32bit.rb
@@ -0,0 +1,14 @@
+# Define cross build settings
+MRuby::CrossBuild.new('cross-32bit') do |conf|
+ conf.toolchain :gcc
+
+ conf.cc.flags << "-m32"
+ conf.linker.flags << "-m32"
+
+ # conf.build_mrbtest_lib_only
+
+ conf.gem :core => "mruby-bin-mruby"
+ conf.gem "#{MRUBY_ROOT}/examples/mrbgems/c_and_ruby_extension_example"
+
+ conf.test_runner.command = 'env'
+end
diff --git a/build_config/cross-mingw.rb b/build_config/cross-mingw.rb
new file mode 100644
index 000000000..63d267d6a
--- /dev/null
+++ b/build_config/cross-mingw.rb
@@ -0,0 +1,14 @@
+#
+# Ubuntu 20.04 requires at least `gcc-mingw-w64-x86-64` package as a
+# cross compiler.
+#
+
+MRuby::CrossBuild.new("cross-mingw") do |conf|
+ conf.toolchain :gcc
+ conf.host_target = "x86_64-w64-mingw32" # required for `for_windows?` used by `mruby-socket` gem
+ conf.cc.command = "#{conf.host_target}-gcc-posix"
+ conf.linker.command = conf.cc.command
+ conf.archiver.command = "#{conf.host_target}-gcc-ar"
+ conf.exts.executable = ".exe"
+ conf.gembox "default"
+end
diff --git a/build_config/default.rb b/build_config/default.rb
new file mode 100644
index 000000000..894f1055b
--- /dev/null
+++ b/build_config/default.rb
@@ -0,0 +1,80 @@
+MRuby::Build.new do |conf|
+ # load specific toolchain settings
+ conf.toolchain
+
+ # Use mrbgems
+ # conf.gem 'examples/mrbgems/ruby_extension_example'
+ # conf.gem 'examples/mrbgems/c_extension_example' do |g|
+ # g.cc.flags << '-g' # append cflags in this gem
+ # end
+ # conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
+ # conf.gem :core => 'mruby-eval'
+ # conf.gem :mgem => 'mruby-onig-regexp'
+ # conf.gem :github => 'mattn/mruby-onig-regexp'
+ # conf.gem :git => '[email protected]:mattn/mruby-onig-regexp.git', :branch => 'master', :options => '-v'
+
+ # include the GEM box
+ 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()
+ # cc.option_include_path = %q[-I"%s"]
+ # cc.option_define = '-D%s'
+ # cc.compile_options = %Q[%{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 = %q[-o "%{outfile}" "%{infile}"]
+ # end
+
+ # gperf settings
+ # conf.gperf do |gperf|
+ # gperf.command = 'gperf'
+ # gperf.compile_options = %q[-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 separator
+ # conf.file_separator = '/'
+
+ # Turn on `enable_debug` for better debugging
+ # conf.enable_debug
+ conf.enable_bintest
+ conf.enable_test
+end
diff --git a/build_config/dreamcast_shelf.rb b/build_config/dreamcast_shelf.rb
new file mode 100644
index 000000000..ee9b260a5
--- /dev/null
+++ b/build_config/dreamcast_shelf.rb
@@ -0,0 +1,119 @@
+# Cross Compiling configuration for the Sega Dreamcast
+# https://dreamcast.wiki/Using_Ruby_for_Sega_Dreamcast_development
+#
+# Requires KallistiOS (KOS)
+# http://gamedev.allusion.net/softprj/kos/
+#
+# Tested on GNU/Linux, macOS and Windows (through MinGW-w64/MSYS2, Cygwin and DreamSDK).
+# DreamSDK is based on MinGW/MSYS: https://dreamsdk.org/
+#
+# Input this command on the directory where mruby is installed:
+# make MRUBY_CONFIG=dreamcast_shelf
+#
+MRuby::CrossBuild.new("dreamcast") do |conf|
+ toolchain :gcc
+
+ # Support for DreamSDK (based on MinGW/MSYS)
+ # To compile mruby with DreamSDK, RubyInstaller for Windows should be installed
+ DREAMSDK_HOME = ENV["DREAMSDK_HOME"]
+ MSYS_ROOT = !(DREAMSDK_HOME.nil? || DREAMSDK_HOME.empty?) ? "#{DREAMSDK_HOME}/msys/1.0" : ""
+
+ # Setting paths
+ DREAMCAST_PATH = "#{MSYS_ROOT}/opt/toolchains/dc"
+ KOS_PATH = "#{DREAMCAST_PATH}/kos"
+ BIN_PATH = "#{DREAMCAST_PATH}/sh-elf/bin"
+
+ # C compiler
+ # Flags were extracted from KallistiOS environment files
+ conf.cc do |cc|
+ cc.command = "#{BIN_PATH}/sh-elf-gcc"
+ cc.include_paths << ["#{KOS_PATH}/include", "#{KOS_PATH}/kernel/arch/dreamcast/include", "#{KOS_PATH}/addons/include", "#{KOS_PATH}/../kos-ports/include"]
+ cc.flags << ["-O2", "-fomit-frame-pointer", "-ml", "-m4-single-only", "-ffunction-sections", "-fdata-sections", "-Wall", "-g", "-fno-builtin", "-ml", "-m4-single-only", "-Wl,-Ttext=0x8c010000", "-Wl,--gc-sections", "-T#{KOS_PATH}/utils/ldscripts/shlelf.xc", "-nodefaultlibs"]
+ cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"]
+ cc.defines << %w(_arch_dreamcast)
+ cc.defines << %w(_arch_sub_pristine)
+ end
+
+ # C++ compiler
+ 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
+
+ # Linker
+ # There is an issue when making the mruby library with KallistiOS:
+ # 'newlib_kill.o' and 'newlib_getpid.o' aren't found so they are explicitly
+ # specified here at least for now.
+ conf.linker do |linker|
+ linker.command="#{BIN_PATH}/sh-elf-gcc"
+ linker.flags << ["#{MSYS_ROOT}/opt/toolchains/dc/kos/kernel/build/newlib_kill.o", "#{MSYS_ROOT}/opt/toolchains/dc/kos/kernel/build/newlib_getpid.o", "-Wl,--start-group -lkallisti -lc -lgcc -Wl,--end-group"]
+ linker.library_paths << ["#{KOS_PATH}/lib/dreamcast", "#{KOS_PATH}/addons/lib/dreamcast", "#{KOS_PATH}/../kos-ports/lib"]
+ end
+
+ # Archiver
+ conf.archiver do |archiver|
+ archiver.command = "#{BIN_PATH}/sh-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
+ # Some Gems are incompatible and were disabled.
+
+ conf.gem :core => "mruby-array-ext"
+ conf.gem :core => "mruby-binding"
+ conf.gem :core => "mruby-binding-core"
+ conf.gem :core => "mruby-catch"
+ conf.gem :core => "mruby-class-ext"
+ conf.gem :core => "mruby-cmath"
+ conf.gem :core => "mruby-compar-ext"
+ conf.gem :core => "mruby-compiler"
+ conf.gem :core => "mruby-complex"
+ conf.gem :core => "mruby-enum-chain"
+ conf.gem :core => "mruby-enum-ext"
+ conf.gem :core => "mruby-enum-lazy"
+ conf.gem :core => "mruby-enumerator"
+ conf.gem :core => "mruby-error"
+ conf.gem :core => "mruby-eval"
+ conf.gem :core => "mruby-exit"
+ conf.gem :core => "mruby-fiber"
+ conf.gem :core => "mruby-hash-ext"
+ conf.gem :core => "mruby-inline-struct"
+# conf.gem :core => "mruby-io"
+ conf.gem :core => "mruby-kernel-ext"
+ conf.gem :core => "mruby-math"
+ conf.gem :core => "mruby-metaprog"
+ conf.gem :core => "mruby-method"
+ conf.gem :core => "mruby-numeric-ext"
+ conf.gem :core => "mruby-object-ext"
+ conf.gem :core => "mruby-objectspace"
+ conf.gem :core => "mruby-os-memsize"
+ conf.gem :core => "mruby-pack"
+ conf.gem :core => "mruby-print"
+ conf.gem :core => "mruby-proc-binding"
+ conf.gem :core => "mruby-proc-ext"
+ conf.gem :core => "mruby-random"
+ conf.gem :core => "mruby-range-ext"
+ conf.gem :core => "mruby-rational"
+ conf.gem :core => "mruby-sleep"
+# conf.gem :core => "mruby-socket"
+ conf.gem :core => "mruby-sprintf"
+ conf.gem :core => "mruby-string-ext"
+ conf.gem :core => "mruby-struct"
+ conf.gem :core => "mruby-symbol-ext"
+# conf.gem :core => "mruby-test"
+# conf.gem :core => "mruby-time"
+ conf.gem :core => "mruby-toplevel-ext"
+end
diff --git a/build_config/gameboyadvance.rb b/build_config/gameboyadvance.rb
new file mode 100644
index 000000000..964dc7af1
--- /dev/null
+++ b/build_config/gameboyadvance.rb
@@ -0,0 +1,73 @@
+# Cross Compiling configuration for the Nintendo GameBoyAdvance.
+# This configuration requires devkitARM
+# https://devkitpro.org/wiki/Getting_Started/devkitARM
+#
+# Tested only on GNU/Linux
+#
+MRuby::CrossBuild.new("gameboyadvance") do |conf|
+ toolchain :gcc
+
+ DEVKITPRO_PATH = "/opt/devkitpro"
+ BIN_PATH = "#{DEVKITPRO_PATH}/devkitARM/bin"
+
+ # C compiler
+ conf.cc do |cc|
+ cc.command = "#{BIN_PATH}/arm-none-eabi-gcc"
+ cc.flags << ["-mthumb-interwork", "-mthumb", "-O2"]
+ cc.compile_options = %(%{flags} -o "%{outfile}" -c "%{infile}")
+ end
+
+ # C++ compiler
+ conf.cxx do |cxx|
+ cxx.command = "#{BIN_PATH}/arm-none-eabi-g++"
+ 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
+
+ # Linker
+ conf.linker do |linker|
+ linker.command = "#{BIN_PATH}/arm-none-eabi-gcc"
+ linker.flags << ["-mthumb-interwork", "-mthumb", "-specs=gba.specs"]
+ 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
+ # removing mruby-io
+ conf.gem core: "mruby-metaprog"
+ conf.gem core: "mruby-pack"
+ 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-compar-ext"
+ 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-enumerator"
+ conf.gem core: "mruby-enum-lazy"
+ conf.gem core: "mruby-toplevel-ext"
+ conf.gem core: "mruby-kernel-ext"
+ conf.gem core: "mruby-class-ext"
+ conf.gem core: "mruby-compiler"
+end
diff --git a/build_config/host-cxx.rb b/build_config/host-cxx.rb
new file mode 100644
index 000000000..15cb0c347
--- /dev/null
+++ b/build_config/host-cxx.rb
@@ -0,0 +1,12 @@
+MRuby::Build.new do |conf|
+ conf.toolchain
+
+ # include the default GEMs
+ conf.gembox 'default'
+
+ # C compiler settings
+ conf.cc.defines = %w(MRB_USE_DEBUG_HOOK)
+ conf.enable_debug
+ conf.enable_cxx_abi
+ conf.enable_test
+end
diff --git a/build_config/host-debug.rb b/build_config/host-debug.rb
new file mode 100644
index 000000000..600a4d78f
--- /dev/null
+++ b/build_config/host-debug.rb
@@ -0,0 +1,20 @@
+MRuby::Build.new('host') do |conf|
+ # load specific toolchain settings
+ conf.toolchain
+
+ conf.enable_debug
+
+ # include the default GEMs
+ conf.gembox 'full-core'
+
+ # C compiler settings
+ conf.cc.defines = %w(MRB_USE_DEBUG_HOOK MRB_NO_BOXING)
+
+ # Generate mruby debugger command (require mruby-eval)
+ conf.gem :core => "mruby-bin-debugger"
+
+ # test
+ conf.enable_test
+ # bintest
+ conf.enable_bintest
+end
diff --git a/build_config/host-f32.rb b/build_config/host-f32.rb
new file mode 100644
index 000000000..a449547ae
--- /dev/null
+++ b/build_config/host-f32.rb
@@ -0,0 +1,14 @@
+MRuby::Build.new do |conf|
+ # load specific toolchain settings
+ toolchain :gcc
+
+ # include the GEM box
+ conf.gembox 'default'
+
+ conf.cc.defines << 'MRB_USE_FLOAT32'
+
+ # Turn on `enable_debug` for better debugging
+ conf.enable_debug
+ conf.enable_test
+ conf.enable_bintest
+end
diff --git a/build_config/host-gprof.rb b/build_config/host-gprof.rb
new file mode 100644
index 000000000..2bdf5bebd
--- /dev/null
+++ b/build_config/host-gprof.rb
@@ -0,0 +1,14 @@
+MRuby::Build.new do |conf|
+ # load specific toolchain settings
+ toolchain :gcc
+
+ # include the GEM box
+ conf.gembox 'default'
+
+ conf.cc.flags << '-pg'
+ conf.linker.flags << '-pg'
+
+ # Turn on `enable_debug` for better debugging
+ conf.enable_debug
+ conf.enable_test
+end
diff --git a/build_config/host-m32.rb b/build_config/host-m32.rb
new file mode 100644
index 000000000..9e27a0792
--- /dev/null
+++ b/build_config/host-m32.rb
@@ -0,0 +1,15 @@
+MRuby::Build.new do |conf|
+ # load specific toolchain settings
+ toolchain :gcc
+
+ # include the GEM box
+ conf.gembox 'default'
+
+ conf.cc.flags << '-m32'
+ conf.linker.flags << '-m32'
+
+ # Turn on `enable_debug` for better debugging
+ conf.enable_debug
+ conf.enable_test
+ conf.enable_bintest
+end
diff --git a/build_config/host-nofloat.rb b/build_config/host-nofloat.rb
new file mode 100644
index 000000000..977bf4e12
--- /dev/null
+++ b/build_config/host-nofloat.rb
@@ -0,0 +1,22 @@
+MRuby::Build.new do |conf|
+ # load specific toolchain settings
+ toolchain :gcc
+
+ # include the GEM box
+ conf.gembox "stdlib"
+ conf.gembox "stdlib-ext"
+ conf.gembox "stdlib-io"
+ conf.gembox "metaprog"
+
+ conf.gem :core => 'mruby-bin-mruby'
+ conf.gem :core => 'mruby-bin-mirb'
+
+ # Add configuration
+ conf.compilers.each do |c|
+ c.defines << "MRB_NO_FLOAT"
+ end
+
+ conf.enable_debug
+ conf.enable_test
+ conf.enable_bintest
+end
diff --git a/build_config/host-shared.rb b/build_config/host-shared.rb
new file mode 100644
index 000000000..38cab8145
--- /dev/null
+++ b/build_config/host-shared.rb
@@ -0,0 +1,36 @@
+MRuby::Build.new do |conf|
+ # load specific toolchain settings
+
+ # Gets set by the VS command prompts.
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+ toolchain :visualcpp
+ else
+ toolchain :gcc
+ end
+
+ # include the GEM box
+ conf.gembox 'default'
+
+ # C compiler settings
+ conf.cc do |cc|
+ cc.flags = '-fPIC'
+ end
+
+ conf.archiver do |archiver|
+ archiver.command = 'gcc'
+ archiver.archive_options = '-shared -o %{outfile} %{objs}'
+ end
+
+ # file extensions
+ conf.exts do |exts|
+ exts.library = '.so'
+ end
+
+ # file separator
+ # conf.file_separator = '/'
+
+ # Turn on `enable_debug` for better debugging
+ conf.enable_debug
+ conf.enable_bintest
+ conf.enable_test
+end
diff --git a/build_config/minimal.rb b/build_config/minimal.rb
new file mode 100644
index 000000000..579b3f883
--- /dev/null
+++ b/build_config/minimal.rb
@@ -0,0 +1,4 @@
+MRuby::CrossBuild.new('minimal') do |conf|
+ conf.toolchain :gcc
+ conf.cc.defines << 'MRB_NO_STDIO'
+end
diff --git a/build_config/mrbc.rb b/build_config/mrbc.rb
new file mode 100644
index 000000000..95444b936
--- /dev/null
+++ b/build_config/mrbc.rb
@@ -0,0 +1,11 @@
+MRuby::Build.new do |conf|
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+ conf.toolchain :visualcpp
+ else
+ conf.toolchain :gcc
+ end
+
+ conf.build_mrbc_exec
+ conf.disable_libmruby
+ conf.disable_presym
+end
diff --git a/build_config/nintendo_switch.rb b/build_config/nintendo_switch.rb
new file mode 100644
index 000000000..549c1f269
--- /dev/null
+++ b/build_config/nintendo_switch.rb
@@ -0,0 +1,73 @@
+# Cross Compiling configuration for the Nintendo Switch, it requires Nintendo SDK
+# Tested on windows
+MRuby::CrossBuild.new('nintendo_switch_32bit') do |conf|
+ conf.toolchain :clang
+ NINTENDO_SDK_PATH = ENV['NINTENDO_SDK_ROOT']
+
+ include_paths = [
+ "#{NINTENDO_SDK_PATH}/Include",
+ "#{NINTENDO_SDK_PATH}/Common/Configs/Targets/NX-NXFP2-a32/Include"
+ ]
+
+ conf.cc do |cc|
+ cc.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++"
+ cc.include_paths += include_paths
+ cc.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float']
+ cc.defines += 'NN_SDK_BUILD_RELEASE'
+ end
+
+ conf.cxx do |cxx|
+ cxx.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++"
+ cxx.include_paths += include_paths
+ cxx.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float']
+ cxx.defines += 'NN_SDK_BUILD_RELEASE'
+ end
+
+ conf.archiver do |archiver|
+ archiver.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/armv7l/bin/llvm-ar"
+ end
+
+ conf.linker do |linker|
+ linker.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/armv7l/bin/clang++"
+ linker.libraries = []
+ end
+
+ # Add your mrbgems
+end
+
+MRuby::CrossBuild.new('nintendo_switch_64bit') do |conf|
+ conf.toolchain :clang
+ NINTENDO_SDK_PATH = ENV['NINTENDO_SDK_ROOT']
+
+ include_paths = [
+ "#{NINTENDO_SDK_PATH}/Include",
+ "#{NINTENDO_SDK_PATH}/Common/Configs/Targets/NX-NXFP2-a64/Include"
+ ]
+
+ conf.cc do |cc|
+ cc.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++"
+ cc.include_paths += include_paths
+ cc.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float']
+ cc.flags << '--target=aarch64-nintendo-nx-elf'
+ cc.defines += 'NN_SDK_BUILD_RELEASE'
+ end
+
+ conf.cxx do |cxx|
+ cxx.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/bin/nx-clang++"
+ cxx.include_paths += include_paths
+ cxx.flags += ['-fpic -fno-short-enums -ffunction-sections -fdata-sections -fno-common -fno-strict-aliasing -fomit-frame-pointer -fno-vectorize -funsigned-char -O2 -g -mno-implicit-float']
+ cxx.flags << '--target=aarch64-nintendo-nx-elf'
+ cxx.defines += 'NN_SDK_BUILD_RELEASE'
+ end
+
+ conf.archiver do |archiver|
+ archiver.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/aarch64/bin/llvm-ar"
+ end
+
+ conf.linker do |linker|
+ linker.command = "#{NINTENDO_SDK_PATH}/Compilers/NX/nx/aarch64/bin/clang++"
+ linker.libraries = []
+ end
+
+ # Add your mrbgems
+end
diff --git a/codespell.txt b/codespell.txt
new file mode 100644
index 000000000..dcc0ddb43
--- /dev/null
+++ b/codespell.txt
@@ -0,0 +1,17 @@
+ans
+ba
+creat
+delet
+disabl
+filetest
+fo
+hel
+hist
+ist
+methid
+nd
+quitt
+remore
+runn
+sting
+upto
diff --git a/doc/guides/compile.md b/doc/guides/compile.md
index 2aaf6f1fe..2923a2676 100644
--- a/doc/guides/compile.md
+++ b/doc/guides/compile.md
@@ -6,41 +6,54 @@ 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```)
+
+* C Compiler (e.g. `gcc` or `clang`)
+* Linker (e.g. `gcc` or `clang`)
+* Archive utility (e.g. `ar`)
+* Ruby 2.5 or later (e.g. `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
+* Git (to update mruby source and integrate mrbgems easier)
+* C++ compiler (to use mrbgems which include `*.cpp`, `*.cxx`, `*.cc`)
+* Bison (to compile `mrbgems/mruby-compiler/core/parse.y`)
+* gperf (to compile `mrbgems/mruby-compiler/core/keywords`)
+
+Note that `bison` bundled with macOS is too old to compile `mruby`.
+Try `brew install bison` and follow the instruction shown to update
+the `$PATH` to compile `mruby`. We also encourage you to upgrade `ruby`
+on macOS in similar manner.
+
+## Build
+
+To compile `mruby` with the default build configuration, just invoke `rake`
+inside of the mruby source root. To generate and execute the test tools call
+`rake test`. To clean all build files call `rake clean`. To see full command
+line on build, call `rake -v`.
+
+You can specify your own configuration file by the `MRUBY_CONFIG` environment
+variable (you can use `CONFIG` for shorthand for `MRUBY_CONFIG`). If the path
+doesn't exist, `build_config/${MRUBY_CONFIG}.rb` is used. The default
+configuration is defined in the `build_config/default.rb` file.
+
+Those build configuration files contain the build configuration of mruby, for
+example:
-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
+ 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```.
+All tools necessary to compile mruby can be set or modified here.
## Build Configuration
-Inside of the *build_config.rb* the following options can be configured
-based on your environment.
+We wish you submit a pull-request to `build_config/PLATFORM.rb`, once you
+created a new configuration for a new platform.
+
+Inside the configuration file, the following options can be
+configured based on your environment.
### Toolchains
@@ -50,54 +63,56 @@ configure the build environment for specific compiler infrastructures.
#### GCC
Toolchain configuration for the GNU C Compiler.
+
```ruby
-toolchain :gcc
+conf.toolchain :gcc
```
#### clang
Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the
GCC toolchain.
+
```ruby
-toolchain :clang
+conf.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),
+[Visual Studio Command Prompt](https://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
+conf.toolchain :visualcpp
```
#### Android
Toolchain configuration for Android.
+
```ruby
-toolchain :android
+conf.toolchain :android
```
Requires the custom standalone Android NDK and the toolchain path
-in ```ANDROID_STANDALONE_TOOLCHAIN```.
+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)
+process. For example,
-To select them declare conf.gem as follows:
-```ruby
-conf.gem "#{root}/mrbgems/mruby-bin-mruby"
-conf.gem "#{root}/mrbgems/mruby-bin-mirb"
-```
+* `mruby`
+* `mirb`
+
+The configuration are done via `mrbgems`. See `Mrbgems` section.
### File Separator
Some environments require a different file separator character. It is possible to
-set the character via ```conf.file_separator```.
+set the character via `conf.file_separator`.
+
```ruby
conf.file_separator = '/'
```
@@ -105,6 +120,7 @@ conf.file_separator = '/'
### C Compiler
Configuration of the C compiler binary, flags and include paths.
+
```ruby
conf.cc do |cc|
cc.command = ...
@@ -119,28 +135,31 @@ end
C Compiler has header searcher to detect installed library.
-If you need a include path of header file use ```search_header_path```:
+If you need an include path of header file use `search_header_path`:
+
```ruby
-# Searches ```iconv.h```.
+# Searches `iconv.h`.
# If found it will return include path of the header file.
-# Otherwise it will return nil .
+# 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```:
+If you need a full file name of header file use `search_header`:
+
```ruby
-# Searches ```iconv.h```.
+# Searches `iconv.h`.
# If found it will return full path of the header file.
-# Otherwise it will return nil .
+# 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.
+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```)
+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:
-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
@@ -150,6 +169,7 @@ end
### Linker
Configuration of the Linker binary, flags and library paths.
+
```ruby
conf.linker do |linker|
linker.command = ...
@@ -167,6 +187,7 @@ end
### Archiver
Configuration of the Archiver binary and flags.
+
```ruby
conf.archiver do |archiver|
archiver.command = ...
@@ -177,6 +198,7 @@ end
### Parser Generator
Configuration of the Parser Generator binary and flags.
+
```ruby
conf.yacc do |yacc|
yacc.command = ...
@@ -187,6 +209,7 @@ end
### GPerf
Configuration of the GPerf binary and flags.
+
```ruby
conf.gperf do |gperf|
gperf.command = ...
@@ -195,6 +218,7 @@ end
```
### File Extensions
+
```ruby
conf.exts do |exts|
exts.object = ...
@@ -203,26 +227,52 @@ conf.exts do |exts|
end
```
+### Preallocated Symbols
+
+By far, preallocated symbols are highly compatible with the previous versions, so
+we expect you won't see any problem with them. But just in case you face any
+issue, you can disable preallocated symbols by specifying `conf.disable_presym`.
+
+In the build process, `mrbc` under cross compiling environment will be compiled
+with this configuration.
+
### Mrbgems
-Integrate GEMs in the build process.
+`mruby` comes with the (sort of) packaging system named `mrbgems`. To
+specify `gem`, you can use `conf.gem` in the configuration file.
+
```ruby
-# Integrate GEM with additional configuration
-conf.gem 'path/to/gem' do |g|
- g.cc.flags << ...
-end
+# Integrate a bundled Gem you see in `mrbgems` directory
+conf.gem :core => 'mruby-something'
+
+# Integrate a Gem from GitHub
+conf.gem :github => 'someone/mruby-another'
+
+# Integrate a mruby binary Gem
+conf.gem :core => 'mruby-bin-mruby'
+
+# Integrate a interactive mruby binary Gem
+conf.gem :core => 'mruby-bin-mirb'
-# Integrate GEM without additional configuration
-conf.gem 'path/to/another/gem'
+# Integrate GemBox (set of Gems)
+conf.gembox "default"
```
+A GemBox is a set of Gems defined in `mrbgems/default.gembox` for example.
+It's just a set of `mrbgem` configurations.
+
+There is a `RubyGem` (gem for CRuby) named `mgem` that help you to
+manage `mrbgems`. Try `gem install mgem`. `mgem` can show you the list
+of registered `mrbgems`.
+
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```
+If you want `mrbtest.a` only, You should set `conf.build_mrbtest_lib_only`
+
```ruby
conf.build_mrbtest_lib_only
```
@@ -230,11 +280,12 @@ 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/```.
+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
```
@@ -247,64 +298,71 @@ 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
+`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.
+#### C++ exception disabling
-If your compiler does not support C++ and you want to ensure
+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.
+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.
+
+* 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:
+mruby can also be cross-compiled from one platform to another. To achieve
+cross-compilation, the build configuration 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.toolchain :gcc
conf.cc.flags << "-m32"
conf.linker.flags << "-m32"
end
```
-All configuration options of ```MRuby::Build``` can also be used
-in ```MRuby::CrossBuild```.
+All configuration options of `MRuby::Build` can also be used in
+`MRuby::CrossBuild`. You can find examples under the `build_config`
+directory.
### Mrbtest in Cross-Compilation
-In cross compilation, you can run ```mrbtest``` on emulator if
+In cross compilation, you can run `mrbtest` on an 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
@@ -318,48 +376,44 @@ end
## Build process
-During the build process the directory *build* will be created in the
+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
+```
++- build
+ |
+ +- host
+ |
+ +- LEGAL <- License description
+ |
+ +- bin <- Binaries (mirb, mrbc and mruby)
+ |
+ +- lib <- Libraries (libmruby.a and libmruby_core.a)
+ |
+ +- mrbc <- Minimal mrbc place
+ |
+ +- mrbgems <- Compilation result from mrbgems
+ |
+ +- mrblib <- Compilation result from mrblib
+ |
+ +- src <- Compilation result from C sources
+```
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*
+
+* compile minimal `mrbc` from `src` and `mrblib` sources
+ * compile all files under `src` (object files will be stored in `build/host/mrbc/src`)
+ * compile `mruby-compiler` gem
+ * create `build/host/mrbc/lib/libmruby_core.a` out of all object files (C only)
+ * create `build/host/mrbc/bin/mrbc` via `mruby-bin-mrbc` gem
+* compile all files under `src` and store result in `build/host/src`
+* create `build/host/mrblib/mrblib.c` by compiling all `*.rb` files under `mrblib` with `build/host/mrbc/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)
+* compile (normal) mrbgems specified in the configuration file
+* create `build/host/lib/libmruby.a` from object files from gems and `libmruby_core.a`
+* create binary commands according to binary gems (e.g. `mirb` and `mruby`)
+* copy binaries under `build/host/bin` to `bin` directory
```
_____ _____ ______ ____ ____ _____ _____ ____
@@ -370,73 +424,54 @@ linking with *build/host/lib/libmruby.a*
### Cross-Compilation
-In case of a cross-compilation to *i386* the *build* directory structure looks
+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
+```
++- build
+ |
+ +- host
+ | |
+ | +- bin <- Native Binaries
+ | |
+ | +- lib <- Native Libraries
+ | |
+ | +- mrbgems
+ | |
+ | +- src
+ |
+ +- i386
+ |
+ +- bin <- Cross-compiled Binaries
+ |
+ +- include <- Header Directory
+ |
+ +- lib <- Cross-compiled Libraries
+ |
+ +- mrbgems
+ |
+ +- mrblib
+ |
+ +- src
+```
An extra directory is created for the target platform. In case you
-compile for *i386* a directory called *i386* is created under the
+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*
+compilation by compiling all *native* libraries and binaries, except
+for we don't have `host/mrbc` directory (`host` directory itself works
+as placeholder for `mrbc`). Afterwards the cross compilation process
+proceeds like this:
+
+* cross-compile all files under `src` and store result in `build/i386/src`
+* create `build/i386/lib/libmruby_core.a` out of C object files
+* create `build/i386/mrblib/mrblib.c` by compiling all `*.rb` files under `mrblib` with native `build/host/bin/mrbc`
+* cross-compile `build/i386/mrblib/mrblib.c` to `build/i386/mrblib/mrblib.o`
+* create `build/i386/lib/libmruby.a` from object files from gems and `libmruby_core.a`
+* create binary commands according to binary gems (e.g. `mirb` and `mruby`)
+* copy binaries under `build/host/bin` to `bin` directory
```
_______________________________________________________________
@@ -463,26 +498,61 @@ linking with *build/i386/lib/libmruby_core.a*
### 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
+feature due to the reason that there are functions (e.g. 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 = []
+MRuby::CrossBuild.new('minimal') do |conf|
+ conf.toolchain :gcc
+ conf.cc.defines << 'MRB_NO_STDIO'
end
```
-This configuration defines a cross compile build called 'Minimal' which
+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).
+all usages of stdio and doesn't compile any binaries (e.g. `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
+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.
+
+## Embedding `mruby` in Your Application
+
+After the build, you will get `libmruby.a`. You can link it to your application.
+
+For compiler options and library path, you can use `mruby-config` command for
+convenience. `mruby-config` command prints the configuration used for `libmruby.a`.
+
+```
+$ mruby-config --help
+Usage: mruby-config [switches]
+ switches:
+ --cc print compiler name
+ --cflags print flags passed to compiler
+ --ld print linker name
+ --ldflags print flags passed to linker
+ --ldflags-before-libs print flags passed to linker before linked libraries
+ --libs print linked libraries
+ --libmruby-path print libmruby path
+ --help print this help
+```
+
+For example, when you have a C source file (`c.c`) and try to
+compile and link it with `libmruby.a`, you can run the following command,
+
+```
+gcc `mruby-config --cflags` c.c `mruby-config --ldflags` `mruby-config --libs`
+```
+
+When you use `make`, add following lines in `Makefile`
+
+```
+MRB_CONFIG = <path-to-mruby-config>
+CFLAGS = `$(MRB_CONFIG) --cflags`
+LDFLAGS = `$(MRB_CONFIG) --ldflags`
+LIBS = `$(MRB_CONFIG) --libs`
+```
diff --git a/doc/guides/debugger.md b/doc/guides/debugger.md
index 1cc7a9a39..14c9bfebd 100644
--- a/doc/guides/debugger.md
+++ b/doc/guides/debugger.md
@@ -38,7 +38,7 @@ To confirm mrdb was installed properly, run mrdb with the `--version` option:
```bash
$ mrdb --version
-mruby 1.4.1 (2018-4-27)
+mruby 3.0.0 (2021-03-05)
```
## 2.2 Basic Operation
@@ -114,8 +114,8 @@ 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 give multiple breakpoints to the specified the line number and method.
+Be aware that the 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.
@@ -165,7 +165,7 @@ Example:
(foo.rb:1) delete
```
-This will delete all of the breakpoints.
+This will delete all the breakpoints.
```
(foo.rb:1) delete 1 3
@@ -192,7 +192,7 @@ Example:
(foo.rb:1) disable
```
-Use `disable` if you would like to disable all of the breakpoints.
+Use `disable` if you would like to disable all the breakpoints.
```
(foo.rb:1) disable 1 3
@@ -220,6 +220,7 @@ Example:
```
Enabling all breakpoints
+
```
(foo.rb:1) enable 1 3
```
@@ -263,7 +264,7 @@ Example:
```
(sample.rb:1) info breakpoints
-Num Type Enb What
+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
@@ -273,8 +274,8 @@ Displaying the specified breakpoint number:
```
(foo.rb:1) info breakpoints 1 3
-Num Type Enb What
-1 breakpoint y at sample.rb:3
+Num Type Enb What
+1 breakpoint y at sample.rb:3
3 breakpoint y in sample_global_method
```
@@ -323,7 +324,7 @@ 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.
+If an exception occurs, the exception information will be displayed, and the debugging will be continued.
Example:
@@ -366,5 +367,5 @@ 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.
+When the method and the block are invoked, the program will stop at the first row.
The program, which is developed in C, will be ignored.
diff --git a/doc/guides/gc-arena-howto.md b/doc/guides/gc-arena-howto.md
index aff7360e9..2c1cdc1fb 100644
--- a/doc/guides/gc-arena-howto.md
+++ b/doc/guides/gc-arena-howto.md
@@ -3,7 +3,7 @@
_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
+[matz blog post]: <https://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
@@ -11,13 +11,13 @@ 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
+words, that it is referenced by somewhere from the 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.
+worry about because GC can access all roots owned by the 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
@@ -38,7 +38,7 @@ 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.
+So we came up with 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.
@@ -46,7 +46,7 @@ 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
+This means that because we cannot collect a 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.
@@ -140,7 +140,7 @@ inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list)
}
```
-This is a real example, so a little bit complicated, but bear with me.
+This is a real example, so slightly 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.
@@ -161,7 +161,7 @@ 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
+We may have an 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
diff --git a/doc/guides/link.md b/doc/guides/link.md
new file mode 100644
index 000000000..8d70330fc
--- /dev/null
+++ b/doc/guides/link.md
@@ -0,0 +1,83 @@
+# Linking `libmruby` to your application
+
+You have two ways to link `libmruby` to your application.
+
+* using executable gem.
+* using normal compilation process
+
+## Executable Gems
+
+If your application is relatively small, `mrbgem` is an easier way to
+create the executable. By tradition, the gem name start with
+`mruby-bin-`, e.g. `mruby-bin-debugger`.
+
+### `mrbgem.rake` file
+
+The executable name is specified in `mrbgem.rake` file at the top of
+your `mrbgem` directory.
+
+```ruby
+MRuby::Gem::Specification.new('mruby-bin-example') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Example for executable command gem'
+ spec.bins = %w(mruby-example) # <- this is binary name
+end
+```
+
+### Source tree structure
+
+The source file for the gem executable should be in
+`<gem-name>/tools/<bin-name>`. Currently, we support C or C++ source code
+(`.c`, `.cpp`, `.cxx`, `.cc`) for the executable. Ruby source files are not
+supported. Put the functionality in the different gem and specify dependency to
+it in `mrbgem.rake`.
+
+## Normal compilation process
+
+The `libmruby` is a normal library so that you can just link it to your
+application. Specify proper compiler options (`-I` etc.) and linker options
+(`-Lmruby` etc.) to compile and link your application. Specify those options in
+your build script (e.g. `Makefile`).
+
+### Compiler options
+
+You need to specify compiler options that are compatible to mruby configuration,
+for example:
+
+* `-I` to specify the place for mruby header files
+* `-D` to specify mruby configuration macros
+
+To retrieve compiler options used to build `mruby`, you can use `mruby-config`
+command with following options:
+
+* `--cc` compiler name
+* `--cflags` options passed to compiler
+
+```
+$ mruby-config --cflags
+-std=gnu99 -g -O3 -Wall -DMRB_GC_FIXED_ARENA -I/home/matz/work/mruby/include -I/home/matz/work/mruby/build/host/include
+```
+
+### Linker options
+
+Just like compiler options, you need to specify linker options that are
+compatible to mruby configuration.
+
+To retrieve linker options, you can use `mruby-config` with following options:
+
+* `--ld` linker name
+* `--ldflags` options passed to linker
+* `--ldflags-before-libs` options passed to linker before linked libraries
+* `--libs` linked libraries
+
+```
+$ mruby-config --ldflags
+-L/home/matz/work/mruby/build/host/lib
+
+$ mruby-config --ldflags-before-libs
+# <nothing in this case>
+
+$ mruby-config --libs
+-lmruby -lm
+```
diff --git a/doc/guides/mrbconf.md b/doc/guides/mrbconf.md
index f957f8ce2..b5bc8c85c 100644
--- a/doc/guides/mrbconf.md
+++ b/doc/guides/mrbconf.md
@@ -1,146 +1,208 @@
-# mruby configuration macros.
+# mruby configuration macros
+
+## How to use these 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.
+ * 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.
+ * 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_NO_STDIO`
-## stdio setting.
-`MRB_DISABLE_STDIO`
* When defined `<stdio.h>` 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.
+ * Compiling mruby script from a file.
* Printing features in **src/print.c**.
-## Debug macros.
-`MRB_ENABLE_DEBUG_HOOK`
+## Debug macros
+
+`MRB_USE_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 `<assert.h>`.
* 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.
+## Primitive type configuration
+
+`MRB_USE_FLOAT32`
-`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`.
+* When defined single precision floating-point type(C type `float`) is used as `mrb_float`.
+* Otherwise, 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`.
+`MRB_NO_FLOAT`
+
+* When defined removes floating-point numbers from mruby.
+* It makes mruby easier to handle in "Microcontroller without FPU" and "Kernel Space".
+
+`MRB_INT32`
+
+* When defined, or `MRB_INT64` are not defined on 32-bit CPU mode, `mrb_int` will be defined as `int32_t`.
* 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.
+* When defined, or `MRB_INT32` are not defined on 64-bit CPU mode, `mrb_int` will be defined as `int64_t`.
+* Conflicts with `MRB_INT32`.
+
+## 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.
+## 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.
+## 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_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`.
+* Conflicts with `MRB_USE_FLOAT32` and `MRB_NO_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.
+## Reduce heap memory configuration
+
+`MRB_USE_LINK_TIME_RO_DATA_P`
+
+* Only available on ELF platforms.
+* If you specify the address of a read-only section when creating a symbol or string, that string will be used as it is.
+* Heap memory can be saved.
+* Uses `__ehdr_start` and `__init_array_start`.
+* It must be `__ehdr_start < data_addr < __init_array_start`.
+
+`MRB_USE_CUSTOM_RO_DATA_P`
+
+* Takes precedence over `MRB_USE_LINK_TIME_RO_DATA_P`.
+* Please try if `MRB_USE_LINK_TIME_RO_DATA_P` is not available.
+* The `mrb_ro_data_p()` function is implemented by the user in an arbitrary file.
+* The prototype declaration is `mrb_bool mrb_ro_data_p(const char *ptr)`.
+* Return `TRUE` if `ptr` is in the read-only section, otherwise return `FALSE`.
+
+## Other configuration
-## 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..
+`MRB_NO_METHOD_CACHE`
+
+* Disable method cache to save memory.
+
+`MRB_METHOD_CACHE_SIZE`
+
+* Default value is `256`.
+* Ignored if `MRB_NO_METHOD_CACHE` is defined.
+* Need to be the power of 2.
+
+`MRB_USE_METHOD_T_STRUCT`
+
+* Use C struct to represent `mrb_method_t`
+* No `MRB_USE_METHOD_T_STRUCT` requires highest 2 bits of function pointers to be zero
+* Define this macro on machines that use higher bits of pointers
+
+`MRB_USE_ALL_SYMBOLS`
+
+* Make it available `Symbol.all_symbols` in `mrbgems/mruby-symbol-ext`
+* Increase heap memory usage.
diff --git a/doc/guides/mrbgems.md b/doc/guides/mrbgems.md
index 0fcc936ed..c08a09f4e 100644
--- a/doc/guides/mrbgems.md
+++ b/doc/guides/mrbgems.md
@@ -1,47 +1,62 @@
# mrbgems
mrbgems is a library manager to integrate C and Ruby extension in an easy and
-standardised way into mruby.
+standardised way into mruby. Conventionally, each mrbgem name is prefixed by
+`mruby-`, e.g. `mruby-time` for a gem that provides `Time` class functionality.
## 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.
+You have to activate mrbgems explicitly in your build configuration. To add
+a gem, add the following line to your build configuration file, for example:
-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:
+You can also use a relative path to specify a gem.
+
```ruby
conf.gem 'examples/mrbgems/ruby_extension_example'
```
+In that case,
+
+* if your build configuration file is in the `build_config` directory, it's
+ relative from `MRUBY_ROOT`.
+* otherwise, it is relative from the directory where your build configuration is.
+
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:
+You can specify the subdirectory 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.
+For specifying the commit hash to checkout use `:checksum_hash` option:
+
+```ruby
+conf.gem mgem: 'mruby-redis', checksum_hash: '3446d19fc4a3f9697b5ddbf2a904f301c42f2f4e'
+```
+
+If there are missing dependencies, mrbgem dependencies solver will reference
+mrbgem from the core or mgem-list.
-To pull all gems from remote GIT repository on build, call ```./minirake -p```,
-or ```./minirake --pull-gems```.
+To pull all gems from remote GIT repository on build, call `rake -p`,
+or `rake --pull-gems`.
NOTE: `:bitbucket` option supports only git. Hg is unsupported in this version.
@@ -49,13 +64,14 @@ NOTE: `:bitbucket` option supports only git. Hg is unsupported in this version.
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
+add each gem to your build configuration 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*
+into mruby, in the same format as if you were adding them to the build config
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*:
+Below we have created a GemBox containing `mruby-time` and `mrbgems-example`:
+
```ruby
MRuby::GemBox.new do |conf|
conf.gem "#{root}/mrbgems/mruby-time"
@@ -64,20 +80,23 @@ 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
+must be saved with a `.gembox` extension inside the `mrbgems` directory 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
+To use this example GemBox, we save it as `custom.gembox` inside the `mrbgems`
+directory in mruby, and add the following to your build configuration 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.
+
+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"
```
@@ -85,36 +104,41 @@ 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.
+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.
+```
++- GEM_NAME <- Name of GEM
+ |
+ +- README.md <- Readme for GEM
+ |
+ +- mrbgem.rake <- GEM Specification
+ |
+ +- include/ <- Header for Ruby extension (will exported)
+ |
+ +- mrblib/ <- Source for Ruby extension
+ |
+ +- src/ <- Source for C extension
+ |
+ +- tools/ <- Source for Executable (in C)
+ |
+ +- test/ <- Test code (Ruby)
+```
+
+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
+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'
@@ -124,10 +148,10 @@ 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
+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
+The following properties can be set inside 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)
@@ -144,6 +168,7 @@ 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'
@@ -153,10 +178,10 @@ MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
# 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.
+ # 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)
+ # 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
@@ -167,6 +192,7 @@ end
The version requirements and default gem information are optional.
Version requirement supports following operators:
+
* '=': is equal
* '!=': is not equal
* '>': is greater
@@ -174,23 +200,25 @@ Version requirement supports following operators:
* '>=': 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'
+ * 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*.
+You can have default gem to use as dependency when it's not defined in your build configuration.
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.
+use `MRuby::Build#gem` in the build configuration 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.
+ * 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'
@@ -204,7 +232,7 @@ end
```
In case your GEM has more complex build requirements you can use
-the following options additionally inside of your GEM specification:
+the following options additionally inside your GEM specification:
* `spec.cc.flags` (C compiler flags)
* `spec.cc.defines` (C compiler defines)
@@ -219,7 +247,7 @@ the following options additionally inside of your GEM specification:
* `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.
+You also can use `spec.mruby.cc` and `spec.mruby.linker` to add extra global parameters for the compiler and linker.
### include_paths and dependency
@@ -230,10 +258,9 @@ 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.
+Exported include_paths are automatically appended to GEM local include_paths by rake.
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
@@ -243,9 +270,10 @@ integrate C libraries into mruby.
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
+by the name of your GEM. If you call your GEM `c_extension_example`, your
initialisation method could look like this:
-```C
+
+```c
void
mrb_c_extension_example_gem_init(mrb_state* mrb) {
struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
@@ -257,10 +285,10 @@ mrb_c_extension_example_gem_init(mrb_state* mrb) {
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
+by the name of your GEM. If you call your GEM `c_extension_example`, your
finalizer method could look like this:
-```C
+```c
void
mrb_c_extension_example_gem_final(mrb_state* mrb) {
free(someone);
@@ -269,54 +297,57 @@ mrb_c_extension_example_gem_final(mrb_state* mrb) {
### Example
- +- c_extension_example/
- |
- +- src/
- | |
- | +- example.c <- C extension source
- |
- +- test/
- | |
- | +- example.rb <- Test code for C extension
- |
- +- mrbgem.rake <- GEM specification
- |
- +- README.md
+```
++- c_extension_example/
+ |
+ +- README.md (Optional)
+ |
+ +- src/
+ | |
+ | +- example.c <- C extension source
+ |
+ +- test/
+ | |
+ | +- example.rb <- Test code for C extension
+ |
+ +- mrbgem.rake <- GEM specification
+```
## 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*
+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
+```
++- ruby_extension_example/
+ |
+ +- README.md (Optional)
+ |
+ +- mrblib/
+ | |
+ | +- example.rb <- Ruby extension source
+ |
+ +- test/
+ | |
+ | +- example.rb <- Test code for Ruby extension
+ |
+ +- mrbgem.rake <- GEM specification
+```
## 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.
+into the `mrblib` folder and all C files into the `src` folder.
-mruby codes under *mrblib* directory would be executed after gem init C
+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*.
@@ -326,20 +357,61 @@ 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
+```
++- c_and_ruby_extension_example/
+ |
+ +- README.md (Optional)
+ |
+ +- 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
+```
+
+## Binary gems
+
+Some gems can generate executables under `bin` directory. Those gems are called
+binary gems. Names of binary gems are conventionally prefixed by `mruby-bin`,
+e.g. `mruby-bin-mirb` and `mruby-bin-strip`.
+
+To specify the name of executable, you need to specify `spec.bins` in the
+`mrbgem.rake`. The entry point `main()` should be in the C source file under
+`tools/<bin>/*.c` where `<bin>` is a name of the executable. C files under the
+`<bin>` directory are compiled and linked to the executable, but not included in
+`libmruby.a`, whereas files under `mrblib` and `src` are.
+
+It is strongly recommended not to include `mrblib` and `src` directories in the
+binary gems, to separate normal gems and binary gems.
+
+### Example
+
+```
++- mruby-bin-example/
+ |
+ +- README.md (Optional)
+ |
+ +- bintest/
+ | |
+ | +- example.rb <- Test code for binary gem
+ |
+ +- mrbgem.rake <- Gem specification
+ |
+ +- mrblib/ <- Source for Ruby extension (Optional)
+ |
+ +- src/ <- Source for C extension (Optional)
+ |
+ +- tools/
+ |
+ +- example/ <- Executable name directory
+ |
+ +- example.c <- Source for Executable (includes main)
+```
diff --git a/doc/guides/symbol.md b/doc/guides/symbol.md
new file mode 100644
index 000000000..8f90ce59f
--- /dev/null
+++ b/doc/guides/symbol.md
@@ -0,0 +1,83 @@
+# Symbols
+
+Symbols in `mruby` C source code is represented by `mrb_sym` which is alias of
+`uint32_t`. Lower 30 bits are used for symbols so that higher 2 bits can be
+used as flags, e.g. `struct mt_elem` in `class.c`.
+
+```c
+struct mt_elem {
+ union mt_ptr ptr;
+ size_t func_p:1;
+ size_t noarg_p:1;
+ mrb_sym key:sizeof(mrb_sym)*8-2;
+};
+```
+
+## C API
+
+We provide following C API for symbols.
+
+### Generate Symbols
+
+#### `mrb_sym mrb_intern(mrb_state*,const char*,size_t)`
+
+Get a symbol from a string.
+
+#### `mrb_sym mrb_intern_check_cstr(mrb_state*,const char*)`
+
+Get a symbol from a NULL terminated (C) string.
+
+#### `mrb_sym mrb_intern_str(mrb_state*,mrb_value)`
+
+Get a symbol from a Ruby string object.
+
+#### `mrb_intern_lit(mrb_state*,const char*)`
+
+Get a symbol from a C string literal. The second argument should be a C string
+literal, otherwise you will get a compilation error. It does not copy C string
+given the fact it's a literal.
+
+#### `mrb_sym mrb_intern_check(mrb_state*,const char*,size_t)`
+
+Get a symbol from a string if the string has been already registered as a
+symbol, otherwise return `0`. We also provide variants `mrb_intern_check_str()`
+(from Ruby string) and `mrb_intern_check_cstr()` (from C string).
+
+#### `const char *mrb_sym_name(mrb_state*,mrb_sym)`
+
+Get a string representation of a symbol as a C string.
+
+#### `const char *mrb_sym_name_len(mrb_state*,mrb_sym,mrb_int*)`
+
+Get a string representation of a symbol, and its length.
+
+## Preallocate Symbols
+
+To save RAM, `mruby` can use compile-time allocation of some symbols. You can
+use following macros to get preallocated symbols by including `mruby/presym.h`
+header.
+
+* `MRB_SYM(xor)` //=> xor (Word characters)
+* `MRB_SYM_B(xor)` //=> xor! (Method with Bang)
+* `MRB_SYM_Q(xor)` //=> xor? (Method with Question mark)
+* `MRB_SYM_E(xor)` //=> xor= (Method with Equal)
+* `MRB_CVSYM(xor)` //=> @@xor (Class Variable)
+* `MRB_IVSYM(xor)` //=> @xor (Instance Variable)
+* `MRB_OPSYM(xor)` //=> ^ (Operator)
+
+For `MRB_OPSYM()`, specify the names corresponding to operators (see
+`MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that
+can be specified for it). Other than that, describe only word characters
+excluding leading and ending punctuations.
+
+These macros are converted to static symbol IDs at compile time, unless
+preallocate symbols are disabled by `conf.disable_presym`. In that case,
+these macros are expanded to `mrb_intern_lit` calls, therefore the mruby state
+variable is required. The above macros assume the variable name is `mrb`. If
+its name is not `mrb`, you need to use macros with `_2` suffix, such as
+`MRB_SYM_2` to specify `mrb_state*` variable.
+
+### Disabling Preallocated Symbols
+
+You can disable preallocated symbols by specifying `conf.disable_presym` in the
+configuration file.
diff --git a/doc/limitations.md b/doc/limitations.md
index 92858cb1f..265e4a2d4 100644
--- a/doc/limitations.md
+++ b/doc/limitations.md
@@ -13,40 +13,9 @@ This document is collecting these limitations.
This document does not contain a complete list of limitations.
Please help to improve it by submitting your findings.
+## `Kernel.raise` in rescue clause
-## ```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 [1.4.1 (2018-4-27)]
-
-```
-[1, 2, 3]
-```
-
-## ```Kernel.raise``` in rescue clause
-
-```Kernel.raise``` without arguments does not raise the current exception within
+`Kernel.raise` without arguments does not raise the current exception within
a rescue clause.
```ruby
@@ -59,26 +28,34 @@ end
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-```ZeroDivisionError``` is raised.
+`ZeroDivisionError` is raised.
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [3.0.0 (2021-03-05)]
-No exception is raised.
+`RuntimeError` is raised instead of `ZeroDivisionError`. To re-raise the exception, you have to do:
+
+```ruby
+begin
+ 1 / 0
+rescue => e
+ raise e
+end
+```
## Fiber execution can't cross C function boundary
-mruby's ```Fiber``` is implemented in a similar way to Lua's co-routine. This
+mruby's `Fiber` is implemented similarly 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.
+Only exception is `mrb_fiber_yield` at return.
-## ```Array``` does not support instance variables
+## `Array` does not support instance variables
-To reduce memory consumption ```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
+ @field = str
end
end
@@ -87,16 +64,16 @@ p Liste.new "foobar"
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-``` [] ```
+` [] `
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [3.0.0 (2021-03-05)]
-```ArgumentError``` is raised.
+`ArgumentError` is raised.
## Method visibility
For simplicity reasons no method visibility (public/private/protected) is
-supported.
+supported. Those methods are defined, but they are dummy methods.
```ruby
class VisibleTest
@@ -119,17 +96,53 @@ false
true
```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [3.0.0 (2021-03-05)]
```
true
true
```
-## defined?
+### Visibility Declaration
-The ```defined?``` keyword is considered too complex to be fully
-implemented. It is recommended to use ```const_defined?``` and
+The declaration form of following visibility methods are not implemented.
+
+* `public`
+* `private`
+* `protected`
+* `module_function`
+
+Especially, `module_function` method is not dummy, but no declaration form.
+
+```
+module TestModule
+ module_function
+ def test_func
+ p 'test_func called'
+ end
+
+ test_func
+end
+
+p 'ok'
+```
+
+#### Ruby [ruby 2.5.5p157 (2019-03-15 revision 67260)]
+
+```
+ok
+```
+
+#### mruby [3.0.0 (2021-03-05)]
+
+```
+test.rb:8: undefined method 'test_func' (NoMethodError)
+```
+
+## `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
@@ -142,11 +155,11 @@ defined?(Foo)
nil
```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [3.0.0 (2021-03-05)]
-```NameError``` is raised.
+`NameError` is raised.
-## ```alias``` on global variables
+## `alias` on global variables
Aliasing a global variable works in CRuby but is not part
of the ISO standard.
@@ -157,9 +170,9 @@ alias $a $__a__
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-``` nil ```
+` nil `
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [3.0.0 (2021-03-05)]
Syntax error
@@ -178,15 +191,15 @@ end
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-```ArgumentError``` is raised.
-The re-defined ```+``` operator does not accept any arguments.
+`ArgumentError` is raised.
+The re-defined `+` operator does not accept any arguments.
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [3.0.0 (2021-03-05)]
-``` 'ab' ```
+` 'ab' `
Behavior of the operator wasn't changed.
-## Kernel#binding is not supported
+## `Kernel#binding` is not supported
`Kernel#binding` method is not supported.
@@ -197,7 +210,7 @@ $ ruby -e 'puts Proc.new {}.binding'
#<Binding:0x00000e9deabb9950>
```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [3.0.0 (2021-03-05)]
```
$ ./bin/mruby -e 'puts Proc.new {}.binding'
@@ -209,8 +222,7 @@ trace (most recent call last):
## 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.
+to make the behavior simpler and less confusing.
#### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]
@@ -219,7 +231,7 @@ $ ruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
[[{"a"=>1}], {:b=>2}]
```
-#### mruby []
+#### mruby [3.0.0 (2021-03-05)]
```
$ ./bin/mruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
@@ -227,3 +239,38 @@ trace (most recent call last):
[0] -e:1
-e:1: keyword argument hash with non symbol keys (ArgumentError)
```
+
+## `nil?` redefinition in conditional expressions
+
+Redefinition of `nil?` is ignored in conditional expressions.
+
+```ruby
+a = "a"
+def a.nil?
+ true
+end
+puts(a.nil? ? "truthy" : "falsy")
+```
+
+Ruby outputs `falsy`. mruby outputs `truthy`.
+
+## 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`.
diff --git a/doc/mruby3.md b/doc/mruby3.md
new file mode 100644
index 000000000..a64e3c73e
--- /dev/null
+++ b/doc/mruby3.md
@@ -0,0 +1,158 @@
+# User visible changes in `mruby3`
+
+# Build System
+
+## `build_config` directory
+
+Typical build configuration files are located in `build_config`
+directory. For examples:
+
+* `default`: the default configuration
+* `host-gprof`: compiles with `gprof` for performance tuning
+* `host-m32`: compiles in gcc 32bit mode on 64bit platforms
+* `boxing`: compiles all three boxing options
+* `clang-asan`: compiles with `clang`'s Address Sanitizer
+
+You can specify the build configuration file with the
+`MRUBY_CONFIG` environment variable (or `CONFIG` in short).
+If the value specified by `MRUBY_CONFIG` is not the path to
+the configuration file, `build_config/${MRUBY_CONFIG}.rb` is
+used. So you can specify it as `rake MRUBY_CONFIG=boxing`,
+for example.
+
+# Build Configuration Contribution
+
+When you write a new build configuration description, please
+contribute. We welcome your contribution as a GitHub
+pull-request.
+
+# Language Changes
+
+## New Syntax
+
+We have ported some new syntax from CRuby.
+
+* Single line pattern matching (`12 => x`);
+ mruby matches only with local variables at the moment
+* Numbered block parameter (`x.map{_1 * 2}`)
+* End-less `def` (`def double(x) = x*2`)
+
+# Configuration Options Changed
+
+## Renamed for consistency
+
+Some configuration macro names are changed for consistency (use `MRB_USE_XXX`
+or `MRB_NO_XXX`).
+
+| mruby2 | mruby3 |
+|--------------------------------|---------------------------|
+| `MRB_ENABLE_ALL_SYMBOLS` | `MRB_USE_ALL_SYMBOLS` |
+| `MRB_ENABLE_CXX_ABI` | `MRB_USE_CXX_ABI` |
+| `MRB_ENABLE_CXX_EXCEPTION` | `MRB_USE_CXX_EXCEPTION` |
+| `MRB_ENABLE_DEBUG_HOOK` | `MRB_USE_DEBUG_HOOK` |
+| `MRB_DISABLE_DIRECT_THREADING` | `MRB_NO_DIRECT_THREADING` |
+| `MRB_DISABLE_STDIO` | `MRB_NO_STDIO` |
+| `MRB_METHOD_T_STRUCT` | `MRB_USE_METHOD_T_STRUCT` |
+| `MRB_USE_FLOAT` | `MRB_USE_FLOAT32` |
+| `MRB_WITHOUT_FLOAT` | `MRB_NO_FLOAT` |
+| `ENABLE_LINENOISE` | `MRB_USE_LINENOISE` |
+| `ENABLE_READLINE` | `MRB_USE_READLINE` |
+| `DISABLE_MIRB_UNDERSCORE` | `MRB_NO_MIRB_UNDERSCORE` |
+
+* `MRB_USE_FLOAT32` is changed from `MRB_USE_FLOAT` to make sure `float` here
+ means using single precision float, and not the opposite of `MRB_NO_FLOAT`.
+* `MRB_USE_METHOD_T_STRUCT` uses `struct` version of `mrb_method_t`. More
+ portable but consumes more memory. Turned on by default on 32bit platforms.
+* `MRB_` prefix is added to those without.
+
+## `MRB_NO_BOXING`
+
+Uses `struct` to represent `mrb_value`. Consumes more memory
+but easier to investigate the internal and to debug. It used
+to be default `mrb_value` representation. Now the default is
+`MRB_WORD_BOXING`.
+
+## `MRB_WORD_BOXING`
+
+Pack `mrb_value` in an `intptr_t` integer. Consumes less
+memory compared to `MRB_NO_BOXING` especially on 32-bit
+platforms. Inlined integer size is 31 bits, so some `mrb_int`
+values does not fit in `mrb_value`. Those integers are allocated
+in the object heap as `struct RInteger`.
+
+## `MRB_NAN_BOXING`
+
+Pack `mrb_value` in a floating-point number. Nothing
+changed from previous versions.
+
+## `MRB_USE_MALLOC_TRIM`
+
+Call `malloc_trim(0)` from mrb_full_gc() if this macro is defined.
+If you are using glibc malloc, this macro could reduce memory consumption.
+
+# Command Line Program
+
+## `bin/mruby` (by mrbgems/mruby-bin-mruby)
+
+The mruby3 now automatically detects `*.mrb` files without the `-b`
+switch. Therefore, it can be mixed with the `*.rb` file in combination
+with the `-r` switch and specified at the same time.
+Here's an example that works fine:
+
+```console
+$ bin/mruby app.mrb
+$ bin/mruby -r lib1.mrb -r lib2.rb app.rb
+$ bin/mruby -r lib1.rb -r lib2.rb < app.mrb
+```
+
+# Internal Changes
+
+## New Instructions
+
+`mruby3` introduces a few new instructions.
+
+Instructions that load a 16/32-bit integer.
+
+* `OP_LOADI16`
+* `OP_LOADI32`
+
+Instruction that unwinds jump table for rescue/ensure.
+
+* `OP_JMPUW`
+
+Renamed from `OP_RAISE`
+
+* `OP_RAISEIF`
+
+Instruction that is reserved for the future keyword argument support.
+
+* OP_SENDVK
+
+## Removed Instructions
+
+Instructions for old exception handling
+
+* `OP_ONERR`
+* `OP_POPERR`
+* `OP_EPUSH`
+* `OP_EPOP`
+
+No more operand extension
+
+* `OP_EXT1`
+* `OP_EXT2`
+* `OP_EXT3`
+
+## Changed Instructions
+
+Jump addresses used to be specified by absolute offset from the start of `iseq`. Now they are relative offset from the address of the next instruction.
+
+## `Random` now use `xoshiro128++`
+
+For better and faster random number generation.
+
+## Preallocated Symbol
+
+Preallocated symbols are interned at compile-time. They can be accessed via symbols macros (e.g. `MRB_SYM()`).
+
+See [Symbols](./guides/symbol.md).
diff --git a/doc/mruby_logo_red_icon.png b/doc/mruby_logo_red_icon.png
new file mode 100644
index 000000000..9006c84eb
--- /dev/null
+++ b/doc/mruby_logo_red_icon.png
Binary files differ
diff --git a/doc/opcode.md b/doc/opcode.md
index fea0afafd..1c13c2931 100644
--- a/doc/opcode.md
+++ b/doc/opcode.md
@@ -1,12 +1,12 @@
# The new bytecode
-We will reimplement VM to use 8bit instruction code. By
+We will reimplement the 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
+Instructions are bytes. There can be 256 instructions. Currently, we
have 94 instructions. Instructions can take 0 to 3 operands.
## operands
@@ -16,112 +16,116 @@ 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) (Syms[b]=:+)
-|OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c) (Syms[b]=:+)
-|OP_SUB" |BB |R(a) = R(a)-R(a+1) (Syms[b]=:-)
-|OP_SUBI" |BB |R(a) = R(a)-C (Syms[b]=:-)
-|OP_MUL" |BB |R(a) = R(a)*R(a+1) (Syms[b]=:*)
-|OP_DIV" |BB |R(a) = R(a)/R(a+1) (Syms[b]=:/)
-|OP_EQ" |BB |R(a) = R(a)==R(a+1) (Syms[b]=:==)
-|OP_LT" |BB |R(a) = R(a)<R(a+1) (Syms[b]=:<)
-|OP_LE" |BB |R(a) = R(a)<=R(a+1) (Syms[b]=:<=)
-|OP_GT" |BB |R(a) = R(a)>R(a+1) (Syms[b]=:>)
-|OP_GE" |BB |R(a) = R(a)>=R(a+1) (Syms[b]=:>=)
-|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
+| Instruction Name | Operand type | Semantics |
+|------------------|--------------|----------------------------------------------------------|
+| `OP_NOP` | `-` | `no operation` |
+| `OP_MOVE` | `BB` | `R(a) = R(b)` |
+| `OP_LOADL` | `BB` | `R(a) = Pool(b)` |
+| `OP_LOADI` | `BB` | `R(a) = mrb_int(b)` |
+| `OP_LOADINEG` | `BB` | `R(a) = mrb_int(-b)` |
+| `OP_LOADI__1` | `B` | `R(a) = mrb_int(-1)` |
+| `OP_LOADI_0` | `B` | `R(a) = mrb_int(0)` |
+| `OP_LOADI_1` | `B` | `R(a) = mrb_int(1)` |
+| `OP_LOADI_2` | `B` | `R(a) = mrb_int(2)` |
+| `OP_LOADI_3` | `B` | `R(a) = mrb_int(3)` |
+| `OP_LOADI_4` | `B` | `R(a) = mrb_int(4)` |
+| `OP_LOADI_5` | `B` | `R(a) = mrb_int(5)` |
+| `OP_LOADI_6` | `B` | `R(a) = mrb_int(6)` |
+| `OP_LOADI_7` | `B` | `R(a) = mrb_int(7)` |
+| `OP_LOADI16` | `BS` | `R(a) = mrb_int(b)` |
+| `OP_LOADI32` | `BSS` | `R(a) = mrb_int((b<<16)+c)` |
+| `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[Syms(b)]` |
+| `OP_SETSV` | `BB` | `Special[Syms(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` | `BS` | `if R(a) pc+=b` |
+| `OP_JMPNOT` | `BS` | `if !R(a) pc+=b` |
+| `OP_JMPNIL` | `BS` | `if R(a)==nil pc+=b` |
+| `OP_JMPUW` | `S` | `unwind_and_jump_to(a)` |
+| `OP_EXCEPT` | `B` | `R(a) = exc` |
+| `OP_RESCUE` | `BB` | `R(b) = R(a).isa?(R(b))` |
+| `OP_RAISEIF` | `B` | `raise(R(a)) if R(a)` |
+| `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(b),R(a+1),...,R(a+c),&R(a+c+1))` |
+| `OP_SENDVK` | `BB` | `R(a) = call(R(a),Syms(b),*R(a+1),**(a+2),&R(a+3))` |
+| `OP_CALL` | `-` | `R(0) = 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=m5:r1:m5:d1:lv4)` |
+| `OP_ENTER` | `W` | `arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1)` |
+| `OP_KEY_P` | `BB` | `R(a) = kdict.key?(Syms(b))` |
+| `OP_KEYEND` | `-` | `raise unless kdict.empty?` |
+| `OP_KARG` | `BB` | `R(a) = kdict[Syms(b)]; kdict.delete(Syms(b))` |
+| `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=m5:r1:m5:d1:lv4)` |
+| `OP_ADD` | `B` | `R(a) = R(a)+R(a+1)` |
+| `OP_ADDI` | `BB` | `R(a) = R(a)+mrb_int(b)` |
+| `OP_SUB` | `B` | `R(a) = R(a)-R(a+1)` |
+| `OP_SUBI` | `BB` | `R(a) = R(a)-mrb_int(b)` |
+| `OP_MUL` | `B` | `R(a) = R(a)*R(a+1)` |
+| `OP_DIV` | `B` | `R(a) = R(a)/R(a+1)` |
+| `OP_EQ` | `B` | `R(a) = R(a)==R(a+1)` |
+| `OP_LT` | `B` | `R(a) = R(a)<R(a+1)` |
+| `OP_LE` | `B` | `R(a) = R(a)<=R(a+1)` |
+| `OP_GT` | `B` | `R(a) = R(a)>R(a+1)` |
+| `OP_GE` | `B` | `R(a) = R(a)>=R(a+1)` |
+| `OP_ARRAY` | `BB` | `R(a) = ary_new(R(a),R(a+1)..R(a+b))` |
+| `OP_ARRAY2` | `BBB` | `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_ARYDUP` | `B` | `R(a) = ary_dup(R(a))` |
+| `OP_AREF` | `BBB` | `R(a) = R(b)[c]` |
+| `OP_ASET` | `BBB` | `R(a)[c] = R(b)` |
+| `OP_APOST` | `BBB` | `*R(a),R(a+1)..R(a+c) = R(a)[b..]` |
+| `OP_INTERN` | `B` | `R(a) = intern(R(a))` |
+| `OP_STRING` | `BB` | `R(a) = str_dup(Pool(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*2-1))` |
+| `OP_HASHADD` | `BB` | `R(a) = hash_push(R(a),R(a+1)..R(a+b*2))` |
+| `OP_HASHCAT` | `B` | `R(a) = hash_cat(R(a),R(a+1))` |
+| `OP_LAMBDA` | `BB` | `R(a) = lambda(Irep(b),OP_L_LAMBDA)` |
+| `OP_BLOCK` | `BB` | `R(a) = lambda(Irep(b),OP_L_BLOCK)` |
+| `OP_METHOD` | `BB` | `R(a) = lambda(Irep(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),Irep[b])` |
+| `OP_DEF` | `BB` | `R(a).newmethod(Syms(b),R(a+1)); R(a) = Syms(b)` |
+| `OP_ALIAS` | `BB` | `alias_method(target_class,Syms(a),Syms(b))` |
+| `OP_UNDEF` | `B` | `undef_method(target_class,Syms(a))` |
+| `OP_SCLASS` | `B` | `R(a) = R(a).singleton_class` |
+| `OP_TCLASS` | `B` | `R(a) = target_class` |
+| `OP_DEBUG` | `BBB` | `print a,b,c` |
+| `OP_ERR` | `B` | `raise(LocalJumpError, Pool(a))` |
+| `OP_EXT1` | `-` | `make 1st operand 16bit` |
+| `OP_EXT2` | `-` | `make 2nd operand 16bit` |
+| `OP_EXT3` | `-` | `make 1st and 2nd operands 16bit` |
+| `OP_STOP` | `-` | `stop VM` |
+|------------------|--------------|----------------------------------------------------------|
diff --git a/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb b/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb
index d3899c301..8ecbef72c 100644
--- a/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb
+++ b/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb
@@ -1,5 +1,5 @@
module CRubyExtension
def CRubyExtension.ruby_method
- puts "A Ruby Extension"
+ puts "#{self}: A Ruby Extension"
end
end
diff --git a/examples/mrbgems/c_and_ruby_extension_example/src/example.c b/examples/mrbgems/c_and_ruby_extension_example/src/example.c
index 7b780d016..1fdb1078d 100644
--- a/examples/mrbgems/c_and_ruby_extension_example/src/example.c
+++ b/examples/mrbgems/c_and_ruby_extension_example/src/example.c
@@ -1,10 +1,14 @@
#include <mruby.h>
+#include <mruby/string.h>
#include <stdio.h>
static mrb_value
mrb_c_method(mrb_state *mrb, mrb_value self)
{
- puts("A C Extension");
+ mrb_value self_str = mrb_str_to_str(mrb, self);
+
+ printf("%s: A C Extension\n", mrb_str_to_cstr(mrb, self_str));
+
return self;
}
diff --git a/examples/mrbgems/c_extension_example/src/example.c b/examples/mrbgems/c_extension_example/src/example.c
index e70ee42f8..727c7c698 100644
--- a/examples/mrbgems/c_extension_example/src/example.c
+++ b/examples/mrbgems/c_extension_example/src/example.c
@@ -1,10 +1,14 @@
#include <mruby.h>
+#include <mruby/string.h>
#include <stdio.h>
static mrb_value
mrb_c_method(mrb_state *mrb, mrb_value self)
{
- puts("A C Extension");
+ mrb_value self_str = mrb_str_to_str(mrb, self);
+
+ printf("%s: A C Extension\n", mrb_str_to_cstr(mrb, self_str));
+
return self;
}
diff --git a/examples/mrbgems/ruby_extension_example/mrblib/example.rb b/examples/mrbgems/ruby_extension_example/mrblib/example.rb
index b07a2b580..b13aa002b 100644
--- a/examples/mrbgems/ruby_extension_example/mrblib/example.rb
+++ b/examples/mrbgems/ruby_extension_example/mrblib/example.rb
@@ -1,5 +1,5 @@
class RubyExtension
def RubyExtension.ruby_method
- puts "A Ruby Extension"
+ puts "#{self}: A Ruby Extension"
end
end
diff --git a/examples/targets/build_config_android_arm64-v8a.rb b/examples/targets/build_config_android_arm64-v8a.rb
deleted file mode 100644
index 70b0f4b97..000000000
--- a/examples/targets/build_config_android_arm64-v8a.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-MRuby::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
diff --git a/examples/targets/build_config_android_armeabi.rb b/examples/targets/build_config_android_armeabi.rb
deleted file mode 100644
index 17330242a..000000000
--- a/examples/targets/build_config_android_armeabi.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-MRuby::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
diff --git a/include/mrbconf.h b/include/mrbconf.h
index cc28acfaa..2a320f371 100644
--- a/include/mrbconf.h
+++ b/include/mrbconf.h
@@ -25,30 +25,73 @@
#endif
/* configuration options: */
-/* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
-//#define MRB_USE_FLOAT
+/* add -DMRB_USE_FLOAT32 to use float instead of double for floating-point numbers */
+//#define MRB_USE_FLOAT32
-/* exclude floating point numbers */
-//#define MRB_WITHOUT_FLOAT
+/* exclude floating-point numbers */
+//#define MRB_NO_FLOAT
-/* add -DMRB_METHOD_CACHE to use method cache to improve performance */
-//#define MRB_METHOD_CACHE
+/* obsolete configuration */
+#if defined(MRB_USE_FLOAT)
+# define MRB_USE_FLOAT32
+#endif
+
+/* obsolete configuration */
+#if defined(MRB_WITHOUT_FLOAT)
+# define MRB_NO_FLOAT
+#endif
+
+#if defined(MRB_USE_FLOAT32) && defined(MRB_NO_FLOAT)
+#error Cannot define MRB_USE_FLOAT32 and MRB_NO_FLOAT at the same time
+#endif
+
+/* add -DMRB_NO_METHOD_CACHE to disable method cache to save memory */
+//#define MRB_NO_METHOD_CACHE
/* size of the method cache (need to be the power of 2) */
-//#define MRB_METHOD_CACHE_SIZE (1<<7)
+//#define MRB_METHOD_CACHE_SIZE (1<<8)
+
+/* add -DMRB_USE_METHOD_T_STRUCT on machines that use higher bits of function pointers */
+/* no MRB_USE_METHOD_T_STRUCT requires highest 2 bits of function pointers to be zero */
+#ifndef MRB_USE_METHOD_T_STRUCT
+ // can't use highest 2 bits of function pointers at least on 32bit
+ // Windows and 32bit Linux.
+# ifdef MRB_32BIT
+# define MRB_USE_METHOD_T_STRUCT
+# endif
+#endif
-/* 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
+/* define on big endian machines; used by MRB_NAN_BOXING, etc. */
+#ifndef MRB_ENDIAN_BIG
+# if (defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN) || \
+ (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# define MRB_ENDIAN_BIG
+# endif
+#endif
-/* add -DMRB_INT16 to use 16bit integer for mrb_int; conflict with MRB_INT64 */
-//#define MRB_INT16
+/* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT32 and MRB_NO_FLOAT */
+//#define MRB_NAN_BOXING
-/* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT16 */
+/* represent mrb_value as a word (natural unit of data for the processor) */
+//#define MRB_WORD_BOXING
+
+/* represent mrb_value as a struct; occupies 2 words */
+//#define MRB_NO_BOXING
+
+/* if no specific boxing type is chosen */
+#if !defined(MRB_NAN_BOXING) && !defined(MRB_WORD_BOXING) && !defined(MRB_NO_BOXING)
+# define MRB_WORD_BOXING
+#endif
+
+/* add -DMRB_INT32 to use 32bit integer for mrb_int; conflict with MRB_INT64;
+ Default for 32-bit CPU mode. */
+//#define MRB_INT32
+
+/* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT32;
+ Default for 64-bit CPU mode (unless using MRB_NAN_BOXING). */
//#define MRB_INT64
/* if no specific integer type is chosen */
-#if !defined(MRB_INT16) && !defined(MRB_INT32) && !defined(MRB_INT64)
+#if !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
@@ -58,14 +101,8 @@
# 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
+/* call malloc_trim(0) from mrb_full_gc() */
+//#define MRB_USE_MALLOC_TRIM
/* string class to handle UTF-8 encoding */
//#define MRB_UTF8_STRING
@@ -76,12 +113,13 @@
/* 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
+/* if __ehdr_start is available, mruby can reduce memory used by symbols */
+//#define MRB_USE_LINK_TIME_RO_DATA_P
-/* 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
+/* if MRB_USE_LINK_TIME_RO_DATA_P does not work,
+ you can try mrb_ro_data_p() that you have implemented yourself in any file;
+ prototype is `mrb_bool mrb_ro_data_p(const char *ptr)` */
+//#define MRB_USE_CUSTOM_RO_DATA_P
/* turn off generational GC by default */
//#define MRB_GC_TURN_OFF_GENERATIONAL
@@ -95,9 +133,6 @@
/* 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
@@ -110,34 +145,82 @@
/* 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 */
+/* -DMRB_NO_XXXX to drop following features */
+//#define MRB_NO_STDIO /* use of stdio */
-/* end of configuration */
+/* -DMRB_USE_XXXX to enable following features */
+//#define MRB_USE_DEBUG_HOOK /* hooks for debugger */
+//#define MRB_USE_ALL_SYMBOLS /* Symbol.all_symbols */
-/* define MRB_DISABLE_XXXX from DISABLE_XXX (for compatibility) */
-#ifdef DISABLE_STDIO
-#define MRB_DISABLE_STDIO
+/* obsolete configurations */
+#ifdef MRB_METHOD_T_STRUCT
+# define MRB_USE_METHOD_T_STRUCT
#endif
-
-/* define MRB_ENABLE_XXXX from ENABLE_XXX (for compatibility) */
-#ifdef ENABLE_DEBUG
-#define MRB_ENABLE_DEBUG_HOOK
+#if defined(DISABLE_STDIO) || defined(MRB_DISABLE_STDIO)
+# define MRB_NO_STDIO
+#endif
+#ifdef MRB_DISABLE_DIRECT_THREADING
+# define MRB_NO_DIRECT_THREADING
+#endif
+#if defined(ENABLE_DEBUG) || defined(MRB_ENABLE_DEBUG_HOOK)
+# define MRB_USE_DEBUG_HOOK
+#endif
+#ifdef MRB_ENABLE_ALL_SYMBOLS
+# define MRB_USE_ALL_SYMBOLS
+#endif
+#ifdef MRB_ENABLE_CXX_ABI
+# define MRB_USE_CXX_ABI
+#endif
+#ifdef MRB_ENABLE_CXX_EXCEPTION
+# define MRB_USE_CXX_EXCEPTION
#endif
-#ifndef MRB_DISABLE_STDIO
+/* end of configuration */
+
+#ifndef MRB_NO_STDIO
# include <stdio.h>
#endif
-#ifndef FALSE
-# define FALSE 0
-#endif
+/*
+** mruby tuning profiles
+**/
+
+/* A profile for micro controllers */
+#if defined(MRB_CONSTRAINED_BASELINE_PROFILE)
+# ifndef MRB_NO_METHOD_CACHE
+# define MRB_NO_METHOD_CACHE
+# endif
-#ifndef TRUE
-# define TRUE 1
+# ifndef KHASH_DEFAULT_SIZE
+# define KHASH_DEFAULT_SIZE 16
+# endif
+
+# ifndef MRB_HEAP_PAGE_SIZE
+# define MRB_HEAP_PAGE_SIZE 256
+# endif
+
+/* A profile for default mruby */
+#elif defined(MRB_BASELINE_PROFILE)
+
+/* A profile for desktop computers or workstations; rich memory! */
+#elif defined(MRB_MAIN_PROFILE)
+# ifndef MRB_METHOD_CACHE_SIZE
+# define MRB_METHOD_CACHE_SIZE (1<<10)
+# endif
+
+# ifndef MRB_HEAP_PAGE_SIZE
+# define MRB_HEAP_PAGE_SIZE 4096
+# endif
+
+/* A profile for server; mruby vm is long life */
+#elif defined(MRB_HIGH_PROFILE)
+# ifndef MRB_METHOD_CACHE_SIZE
+# define MRB_METHOD_CACHE_SIZE (1<<12)
+# endif
+
+# ifndef MRB_HEAP_PAGE_SIZE
+# define MRB_HEAP_PAGE_SIZE 4096
+# endif
#endif
#endif /* MRUBYCONF_H */
diff --git a/include/mruby.h b/include/mruby.h
index 8eff31746..93aab5672 100644
--- a/include/mruby.h
+++ b/include/mruby.h
@@ -1,7 +1,7 @@
/*
** mruby - An embeddable Ruby implementation
**
-** Copyright (c) mruby developers 2010-2018
+** Copyright (c) mruby developers 2010-2021
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
@@ -22,9 +22,13 @@
** 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 ]
+** [ MIT license: https://www.opensource.org/licenses/mit-license.php ]
*/
+/**
+ * @file mruby.h
+ */
+
#ifndef MRUBY_H
#define MRUBY_H
@@ -34,6 +38,7 @@
#define __STDC_FORMAT_MACROS
#endif
+#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
@@ -48,6 +53,10 @@
#endif
#endif
+#ifdef _MSC_VER
+# define __func__ __FUNCTION__
+#endif
+
#ifdef MRB_DEBUG
#include <assert.h>
#define mrb_assert(p) assert(p)
@@ -57,15 +66,37 @@
#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)
+#if (defined __cplusplus && __cplusplus >= 201103L) || \
+ (defined _MSC_VER) || \
+ (defined __GXX_EXPERIMENTAL_CXX0X__) /* for old G++/Clang++ */
+# define mrb_static_assert(exp, str) static_assert(exp, str)
+#elif defined __STDC_VERSION__ && \
+ ((__STDC_VERSION__ >= 201112L) || \
+ (defined __GNUC__ && __GNUC__ * 100 + __GNUC_MINOR__ >= 406))
+# define mrb_static_assert(exp, str) _Static_assert(exp, str)
#else
-#define mrb_static_assert(exp, str) mrb_assert(exp)
+# /* alternative implementation of static_assert() */
+# define _mrb_static_assert_cat0(a, b) a##b
+# define _mrb_static_assert_cat(a, b) _mrb_static_assert_cat0(a, b)
+# ifdef __COUNTER__
+# define _mrb_static_assert_id(prefix) _mrb_static_assert_cat(prefix, __COUNTER__)
+# else
+# define _mrb_static_assert_id(prefix) _mrb_static_assert_cat(prefix, __LINE__)
+# endif
+# define mrb_static_assert(exp, str) \
+ struct _mrb_static_assert_id(_mrb_static_assert_) { char x[(exp) ? 1 : -1]; }
#endif
+#define mrb_static_assert1(exp) mrb_static_assert(exp, #exp)
#include "mrbconf.h"
-#ifndef MRB_WITHOUT_FLOAT
+#include <mruby/common.h>
+#include <mruby/value.h>
+#include <mruby/gc.h>
+#include <mruby/version.h>
+
+#ifndef MRB_NO_FLOAT
+#include <float.h>
#ifndef FLT_EPSILON
#define FLT_EPSILON (1.19209290e-07f)
#endif
@@ -76,18 +107,13 @@
#define LDBL_EPSILON (1.08420217248550443401e-19L)
#endif
-#ifdef MRB_USE_FLOAT
+#ifdef MRB_USE_FLOAT32
#define MRB_FLOAT_EPSILON FLT_EPSILON
#else
#define MRB_FLOAT_EPSILON DBL_EPSILON
#endif
#endif
-#include "mruby/common.h"
-#include <mruby/value.h>
-#include <mruby/gc.h>
-#include <mruby/version.h>
-
/**
* MRuby C API entry point
*/
@@ -96,11 +122,14 @@ MRB_BEGIN_DECL
typedef uint8_t mrb_code;
/**
- * Required arguments signature type.
+ * \class mrb_aspec
+ *
+ * Specifies the number of arguments a function takes
+ *
+ * Example: `MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1)` for a method that expects 2..3 arguments
*/
typedef uint32_t mrb_aspec;
-
struct mrb_irep;
struct mrb_state;
@@ -121,16 +150,15 @@ typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud);
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;
+ int16_t argc;
+ int16_t acc;
+ const struct RProc *proc;
+ mrb_value *stack;
+ const mrb_code *pc; /* current address on iseq of this proc */
+ union {
+ struct REnv *env;
+ struct RClass *target_class;
+ } u;
} mrb_callinfo;
enum mrb_fiber_state {
@@ -145,37 +173,40 @@ enum mrb_fiber_state {
struct mrb_context {
struct mrb_context *prev;
- mrb_value *stack; /* stack of virtual machine */
- mrb_value *stbase, *stend;
+ mrb_value *stbase, *stend; /* stack of virtual machine */
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;
+ enum mrb_fiber_state status : 4;
+ mrb_bool vmexec : 1;
struct RFiber *fib;
};
#ifdef MRB_METHOD_CACHE_SIZE
-# define MRB_METHOD_CACHE
+# undef MRB_NO_METHOD_CACHE
#else
-/* default method cache size: 128 */
+/* default method cache size: 256 */
/* cache size needs to be power of 2 */
-# define MRB_METHOD_CACHE_SIZE (1<<7)
+# define MRB_METHOD_CACHE_SIZE (1<<8)
#endif
-typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value);
+/**
+ * Function pointer type for a function callable by mruby.
+ *
+ * The arguments to the function are stored on the mrb_state. To get them see mrb_get_args
+ *
+ * @param mrb The mruby state
+ * @param self The self object
+ * @return [mrb_value] The function's return value
+ */
+typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value self);
-#ifdef MRB_METHOD_TABLE_INLINE
+#ifndef MRB_USE_METHOD_T_STRUCT
typedef uintptr_t mrb_method_t;
#else
typedef struct {
- mrb_bool func_p;
+ uint8_t flags;
union {
struct RProc *proc;
mrb_func_t func;
@@ -183,7 +214,7 @@ typedef struct {
} mrb_method_t;
#endif
-#ifdef MRB_METHOD_CACHE
+#ifndef MRB_NO_METHOD_CACHE
struct mrb_cache_entry {
struct RClass *c, *c0;
mrb_sym mid;
@@ -195,13 +226,9 @@ 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 */
@@ -221,31 +248,33 @@ typedef struct mrb_state {
struct RClass *hash_class;
struct RClass *range_class;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
struct RClass *float_class;
#endif
- struct RClass *fixnum_class;
+ struct RClass *integer_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
+#ifndef MRB_NO_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 */
+ mrb_sym symhash[256];
size_t symcapa;
+#ifndef MRB_USE_ALL_SYMBOLS
+ char symbuf[8]; /* buffer for small symbol names */
+#endif
-#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);
+#ifdef MRB_USE_DEBUG_HOOK
+ void (*code_fetch_hook)(struct mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
+ void (*debug_op_hook)(struct mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
#endif
#ifdef MRB_BYTECODE_DECODE_OPTION
@@ -257,7 +286,7 @@ typedef struct mrb_state {
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 */
+ struct RObject *arena_err; /* pre-allocated arena overflow error */
#endif
void *ud; /* auxiliary data */
@@ -267,7 +296,7 @@ typedef struct mrb_state {
#else
mrb_atexit_func *atexit_stack;
#endif
- mrb_int atexit_stack_len;
+ uint16_t atexit_stack_len;
} mrb_state;
/**
@@ -285,23 +314,27 @@ typedef struct mrb_state {
* //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.
+ * @param mrb The current mruby state.
+ * @param name The name of the defined class.
+ * @param 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);
+MRB_API struct RClass *mrb_define_class_id(mrb_state *mrb, mrb_sym 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.
+ * @param mrb The current mruby state.
+ * @param name 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);
+MRB_API struct RClass *mrb_define_module(mrb_state *mrb, const char *name);
+MRB_API struct RClass *mrb_define_module_id(mrb_state *mrb, mrb_sym name);
+
+MRB_API mrb_value mrb_singleton_class(mrb_state *mrb, mrb_value val);
+MRB_API struct RClass *mrb_singleton_class_ptr(mrb_state *mrb, mrb_value val);
/**
* Include a module in another class or module.
@@ -310,11 +343,11 @@ MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value);
* 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.
+ * @param mrb The current mruby state.
+ * @param cla A reference to module or a class.
+ * @param included A reference to the module to be included.
*/
-MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
+MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *cla, struct RClass *included);
/**
* Prepends a module in another class or module.
@@ -323,11 +356,11 @@ MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
* 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.
+ * @param mrb The current mruby state.
+ * @param cla A reference to module or a class.
+ * @param prepended A reference to the module to be prepended.
*/
-MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
+MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *cla, struct RClass *prepended);
/**
* Defines a global function in ruby.
@@ -336,7 +369,6 @@ MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
*
* Example:
*
- * !!!c
* mrb_value example_method(mrb_state* mrb, mrb_value self)
* {
* puts("Executing example command!");
@@ -348,13 +380,14 @@ MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
* 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.
+ * @param mrb The MRuby state reference.
+ * @param cla The class pointer where the method will be defined.
+ * @param name The name of the method being defined.
+ * @param func The function pointer to the method definition.
+ * @param 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);
+MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec);
/**
* Defines a class method.
@@ -375,14 +408,22 @@ MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *n
* 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.
+ * @param mrb The MRuby state reference.
+ * @param cla The class where the class method will be defined.
+ * @param name The name of the class method being defined.
+ * @param fun The function pointer to the class method definition.
+ * @param aspec The method parameters declaration.
+ */
+MRB_API void mrb_define_class_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t fun, mrb_aspec aspec);
+MRB_API void mrb_define_class_method_id(mrb_state *mrb, struct RClass *cla, mrb_sym name, mrb_func_t fun, mrb_aspec aspec);
+
+/**
+ * Defines a singleton method
+ *
+ * @see mrb_define_class_method
*/
-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);
+MRB_API void mrb_define_singleton_method(mrb_state *mrb, struct RObject *cla, const char *name, mrb_func_t fun, mrb_aspec aspec);
+MRB_API void mrb_define_singleton_method_id(mrb_state *mrb, struct RObject *cla, mrb_sym name, mrb_func_t fun, mrb_aspec aspec);
/**
* Defines a module function.
@@ -403,13 +444,14 @@ MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char
* 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.
+ * @param mrb The MRuby state reference.
+ * @param cla The module where the module function will be defined.
+ * @param name The name of the module function being defined.
+ * @param fun The function pointer to the module function definition.
+ * @param aspec The method parameters declaration.
*/
-MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
+MRB_API void mrb_define_module_function(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t fun, mrb_aspec aspec);
+MRB_API void mrb_define_module_function_id(mrb_state *mrb, struct RClass *cla, mrb_sym name, mrb_func_t fun, mrb_aspec aspec);
/**
* Defines a constant.
@@ -432,12 +474,13 @@ MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*,
* 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.
+ * @param mrb The MRuby state reference.
+ * @param cla A class or module the constant is defined in.
+ * @param name The name of the constant being defined.
+ * @param val The value for the constant.
*/
-MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value);
+MRB_API void mrb_define_const(mrb_state* mrb, struct RClass* cla, const char *name, mrb_value val);
+MRB_API void mrb_define_const_id(mrb_state* mrb, struct RClass* cla, mrb_sym name, mrb_value val);
/**
* Undefines a method.
@@ -483,11 +526,11 @@ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_
*
* 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.
+ * @param mrb The mruby state reference.
+ * @param cla The class the method will be undefined from.
+ * @param name 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(mrb_state *mrb, struct RClass *cla, const char *name);
MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym);
/**
@@ -523,11 +566,12 @@ MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym);
* 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.
+ * @param mrb The mruby state reference.
+ * @param cls A class the class method will be undefined from.
+ * @param name The name of the class method to be undefined.
*/
-MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
+MRB_API void mrb_undef_class_method(mrb_state *mrb, struct RClass *cls, const char *name);
+MRB_API void mrb_undef_class_method_id(mrb_state *mrb, struct RClass *cls, mrb_sym name);
/**
* Initialize a new object instance of c class.
@@ -551,10 +595,10 @@ MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
* 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
+ * @param mrb The current mruby state.
+ * @param c Reference to the class of the new object.
+ * @param argc Number of arguments in argv
+ * @param 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);
@@ -565,8 +609,6 @@ MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const
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.
*
@@ -582,8 +624,8 @@ MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv);
* mrb_p(mrb, obj); // => Kernel#p
* }
*
- * @param [mrb_state*] mrb The current mruby state.
- * @param [struct RClass *] super The super class or parent.
+ * @param mrb The current mruby state.
+ * @param 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);
@@ -599,7 +641,7 @@ MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super);
* example_module = mrb_module_new(mrb);
* }
*
- * @param [mrb_state*] mrb The current mruby state.
+ * @param mrb The current mruby state.
* @return [struct RClass *] Reference to the new module.
*/
MRB_API struct RClass * mrb_module_new(mrb_state *mrb);
@@ -626,27 +668,30 @@ MRB_API struct RClass * mrb_module_new(mrb_state *mrb);
* }
* }
*
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name A string representing the name of the class.
+ * @param mrb The current mruby state.
+ * @param 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);
+MRB_API mrb_bool mrb_class_defined_id(mrb_state *mrb, mrb_sym name);
/**
* Gets a class.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name The name of the class.
+ * @param mrb The current mruby state.
+ * @param 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);
+MRB_API struct RClass* mrb_class_get(mrb_state *mrb, const char *name);
+MRB_API struct RClass* mrb_class_get_id(mrb_state *mrb, mrb_sym name);
/**
* Gets a exception class.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name The name of the class.
+ * @param mrb The current mruby state.
+ * @param 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);
+MRB_API struct RClass* mrb_exc_get_id(mrb_state *mrb, mrb_sym name);
+#define mrb_exc_get(mrb, name) mrb_exc_get_id(mrb, mrb_intern_cstr(mrb, name))
/**
* Returns an mrb_bool. True if inner class was defined, and false if the inner class was not defined.
@@ -672,38 +717,46 @@ MRB_API struct RClass * mrb_exc_get(mrb_state *mrb, const char *name);
* }
* }
*
- * @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.
+ * @param mrb The current mruby state.
+ * @param outer The name of the outer class.
+ * @param 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);
+MRB_API mrb_bool mrb_class_defined_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym 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.
+ * @param mrb The current mruby state.
+ * @param outer The name of the parent class.
+ * @param 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);
+MRB_API struct RClass * mrb_class_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name);
/**
* Gets a module.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name The name of the module.
+ * @param mrb The current mruby state.
+ * @param 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);
+MRB_API struct RClass * mrb_module_get_id(mrb_state *mrb, mrb_sym 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.
+ * @param mrb The current mruby state.
+ * @param outer The name of the outer module.
+ * @param 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 struct RClass * mrb_module_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name);
+
+/* a function to raise NotImplementedError with current method name */
+MRB_API void mrb_notimplement(mrb_state*);
+/* a function to be replacement of unimplemented method */
MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
/**
@@ -711,12 +764,11 @@ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
*
* Equivalent to:
* Object#dup
- * @param [mrb_state*] mrb The current mruby state.
- * @param [mrb_value] obj Object to be duplicate.
+ * @param mrb The current mruby state.
+ * @param 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);
-MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method);
/**
* Returns true if obj responds to the given method. If the method was defined for that
@@ -753,9 +805,9 @@ MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char
* }
* }
*
- * @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.
+ * @param mrb The current mruby state.
+ * @param c A reference to a class.
+ * @param 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);
@@ -763,16 +815,18 @@ MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mi
/**
* 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
+ * @param mrb The current mruby state.
+ * @param outer Reference to the module under which the new class will be defined
+ * @param name The name of the defined class
+ * @param 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_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super);
+MRB_API struct RClass* mrb_define_class_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name, struct RClass *super);
-MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name);
+MRB_API struct RClass* mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name);
+MRB_API struct RClass* mrb_define_module_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name);
/**
* Function requires n arguments.
@@ -836,38 +890,93 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
* | `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} | |
+ * | `s` | {String} | const char *, {mrb_int} | Receive two arguments; `s!` gives (`NULL`,`0`) for `nil` |
+ * | `z` | {String} | const char * | `NULL` terminated string; `z!` gives `NULL` for `nil` |
+ * | `a` | {Array} | const {mrb_value} *, {mrb_int} | Receive two arguments; `a!` gives (`NULL`,`0`) for `nil` |
+ * | `f` | {Integer}/{Float} | {mrb_float} | |
+ * | `i` | {Integer}/{Float} | {mrb_int} | |
* | `b` | boolean | {mrb_bool} | |
- * | `n` | {Symbol} | {mrb_sym} | |
+ * | `n` | {String}/{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; when `!` follows, the value may be `nil` |
+ * | `I` | inline struct | void * | |
* | `&` | 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. |
- * | &vert; | optional | | After this spec following specs would be optional. |
+ * | `*` | rest arguments | const {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; `*!` avoid copy of the stack. |
+ * | <code>\|</code> | 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. |
+ * | `:` | keyword args | {mrb_kwargs} const | Get keyword arguments. @see mrb_kwargs |
*
* @see mrb_get_args
*/
typedef const char *mrb_args_format;
/**
- * Retrieve arguments from mrb_state.
+ * Get keyword arguments by `mrb_get_args()` with `:` specifier.
+ *
+ * `mrb_kwargs::num` indicates that the number of keyword values.
+ *
+ * `mrb_kwargs::values` is an object array, and the keyword argument corresponding to the string array is assigned.
+ * Note that `undef` is assigned if there is no keyword argument corresponding to `mrb_kwargs::optional`.
+ *
+ * `mrb_kwargs::table` accepts a string array.
+ *
+ * `mrb_kwargs::required` indicates that the specified number of keywords starting from the beginning of the string array are required.
*
- * When applicable, implicit conversions (such as `to_str`, `to_ary`, `to_hash`) are
- * applied to received arguments.
- * Used inside a function of mrb_func_t type.
+ * `mrb_kwargs::rest` is the remaining keyword argument that can be accepted as `**rest` in Ruby.
+ * If `NULL` is specified, `ArgumentError` is raised when there is an undefined keyword.
+ *
+ * Examples:
+ *
+ * // def method(a: 1, b: 2)
+ *
+ * uint32_t kw_num = 2;
+ * const char *kw_names[kw_num] = { "a", "b" };
+ * uint32_t kw_required = 0;
+ * mrb_value kw_values[kw_num];
+ * const mrb_kwargs kwargs = { kw_num, kw_required, kw_names, kw_values, NULL };
+ *
+ * mrb_get_args(mrb, ":", &kwargs);
+ * if (mrb_undef_p(kw_values[0])) { kw_values[0] = mrb_fixnum_value(1); }
+ * if (mrb_undef_p(kw_values[1])) { kw_values[1] = mrb_fixnum_value(2); }
+ *
+ *
+ * // def method(str, x:, y: 2, z: "default string", **opts)
+ *
+ * mrb_value str, kw_rest;
+ * uint32_t kw_num = 3;
+ * const mrb_sym kw_names[kw_num] = { MRB_SYM(x), MRB_SYM(y), MRB_SYM(z) };
+ * uint32_t kw_required = 1;
+ * mrb_value kw_values[kw_num];
+ * const mrb_kwargs kwargs = { kw_num, kw_required, kw_names, kw_values, &kw_rest };
+ *
+ * mrb_get_args(mrb, "S:", &str, &kwargs);
+ * // or: mrb_get_args(mrb, ":S", &kwargs, &str);
+ * if (mrb_undef_p(kw_values[1])) { kw_values[1] = mrb_fixnum_value(2); }
+ * if (mrb_undef_p(kw_values[2])) { kw_values[2] = mrb_str_new_cstr(mrb, "default string"); }
+ */
+typedef struct mrb_kwargs mrb_kwargs;
+
+struct mrb_kwargs
+{
+ uint32_t num; /* number of keyword arguments */
+ uint32_t required; /* number of required keyword arguments */
+ const mrb_sym *table; /* C array of symbols for keyword names */
+ mrb_value *values; /* keyword argument values */
+ mrb_value *rest; /* keyword rest (dict) */
+};
+
+/**
+ * Retrieve arguments from mrb_state.
*
* @param mrb The current MRuby state.
- * @param format [mrb_args_format] is a list of format specifiers
+ * @param 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
+ * @see mrb_kwargs
*/
MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...);
-static inline mrb_sym
+MRB_INLINE mrb_sym
mrb_get_mid(mrb_state *mrb) /* get method symbol */
{
return mrb->c->ci->mid;
@@ -880,7 +989,25 @@ mrb_get_mid(mrb_state *mrb) /* get method symbol */
*/
MRB_API mrb_int mrb_get_argc(mrb_state *mrb);
-MRB_API mrb_value* mrb_get_argv(mrb_state *mrb);
+/**
+ * Retrieve an array of arguments from mrb_state.
+ *
+ * Correctly handles *splat arguments.
+ */
+MRB_API const mrb_value *mrb_get_argv(mrb_state *mrb);
+
+/**
+ * Retrieve the first and only argument from mrb_state.
+ * Raises ArgumentError unless the number of arguments is exactly one.
+ *
+ * Correctly handles *splat arguments.
+ */
+MRB_API mrb_value mrb_get_arg1(mrb_state *mrb);
+
+/**
+ * Check if a block argument is given from mrb_state.
+ */
+MRB_API mrb_bool mrb_block_given_p(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.
@@ -893,6 +1020,8 @@ MRB_API mrb_value* mrb_get_argv(mrb_state *mrb);
/**
* Call existing ruby functions.
*
+ * Example:
+ *
* #include <stdio.h>
* #include <mruby.h>
* #include "mruby/compile.h"
@@ -907,17 +1036,20 @@ MRB_API mrb_value* mrb_get_argv(mrb_state *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));
+ * mrb_funcall_id(mrb, obj, MRB_SYM(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.
+ * }
+ *
+ * @param mrb The current mruby state.
+ * @param val A reference to an mruby value.
+ * @param name The name of the method.
+ * @param argc The number of arguments the method has.
+ * @param ... Variadic values(not type safe!).
+ * @return [mrb_value] mruby function value.
*/
-MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
+MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value val, const char *name, mrb_int argc, ...);
+MRB_API mrb_value mrb_funcall_id(mrb_state *mrb, mrb_value val, mrb_sym mid, mrb_int argc, ...);
/**
* Call existing ruby functions. This is basically the type safe version of mrb_funcall.
*
@@ -927,54 +1059,69 @@ MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
* int
* main()
* {
- * mrb_int i = 99;
* mrb_state *mrb = mrb_open();
+ * mrb_value obj = mrb_fixnum_value(1);
*
* 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.
+ * mrb_funcall_argv(mrb, obj, MRB_SYM(method_name), 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.
+ * @param mrb The current mruby state.
+ * @param val A reference to an mruby value.
+ * @param name_sym The symbol representing the method.
+ * @param argc The number of arguments the method has.
+ * @param obj 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*);
+MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_int argc, const mrb_value *argv);
/**
* 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);
+MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_int argc, const mrb_value *argv, mrb_value block);
/**
- * Create a symbol
+ * Create a symbol from C string. But usually it's better to use MRB_SYM,
+ * MRB_OPSYM, MRB_CVSYM, MRB_IVSYM, MRB_SYM_B, MRB_SYM_Q, MRB_SYM_E macros.
+ *
+ * Example:
*
* # 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.
+ * mrb_sym sym1 = mrb_intern_lit(mrb, "pizza"); // => :pizza
+ * mrb_sym sym2 = MRB_SYM(pizza); // => :pizza
+ * mrb_sym sym3 = MRB_SYM_Q(pizza); // => :pizza?
+ *
+ * @param mrb The current mruby state.
+ * @param str The string to be symbolized
* @return [mrb_sym] mrb_sym A symbol.
*/
-MRB_API mrb_sym mrb_intern_cstr(mrb_state*,const char*);
+MRB_API mrb_sym mrb_intern_cstr(mrb_state *mrb, const char* str);
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))
+#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_intern_check series functions returns 0 if the symbol is not defined */
+MRB_API mrb_sym mrb_intern_check_cstr(mrb_state*,const char*);
+MRB_API mrb_sym mrb_intern_check(mrb_state*,const char*,size_t);
+MRB_API mrb_sym mrb_intern_check_str(mrb_state*,mrb_value);
+/* mrb_check_intern series functions returns nil if the symbol is not defined */
+/* otherwise returns 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 const char *mrb_sym_name(mrb_state*,mrb_sym);
+MRB_API const char *mrb_sym_name_len(mrb_state*,mrb_sym,mrb_int*);
+MRB_API const char *mrb_sym_dump(mrb_state*,mrb_sym);
+MRB_API mrb_value mrb_sym_str(mrb_state*,mrb_sym);
+#define mrb_sym2name(mrb,sym) mrb_sym_name(mrb,sym)
+#define mrb_sym2name_len(mrb,sym,len) mrb_sym_name_len(mrb,sym,len)
+#define mrb_sym2str(mrb,sym) mrb_sym_str(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 */
@@ -984,6 +1131,17 @@ MRB_API void *mrb_malloc_simple(mrb_state*, size_t); /* return NULL if no memor
MRB_API struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*);
MRB_API void mrb_free(mrb_state*, void*);
+/**
+ * Allocates a Ruby object that matches the constant literal defined in
+ * `enum mrb_vtype` and returns a pointer to the corresponding C type.
+ *
+ * @param mrb The current mruby state
+ * @param tt The constant literal of `enum mrb_vtype`
+ * @param klass A Class object
+ * @return Reference to the newly created object
+ */
+#define MRB_OBJ_ALLOC(mrb, tt, klass) ((MRB_VTYPE_TYPEOF(tt)*)mrb_obj_alloc(mrb, tt, klass))
+
MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len);
/**
@@ -993,14 +1151,20 @@ 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))
+MRB_API mrb_value mrb_obj_freeze(mrb_state*, mrb_value);
+#define mrb_str_new_frozen(mrb,p,len) mrb_obj_freeze(mrb,mrb_str_new(mrb,p,len))
+#define mrb_str_new_cstr_frozen(mrb,p) mrb_obj_freeze(mrb,mrb_str_new_cstr(mrb,p))
+#define mrb_str_new_static_frozen(mrb,p,len) mrb_obj_freeze(mrb,mrb_str_new_static(mrb,p,len))
+#define mrb_str_new_lit_frozen(mrb,lit) mrb_obj_freeze(mrb,mrb_str_new_lit(mrb,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_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
@@ -1055,11 +1219,10 @@ MRB_API void mrb_close(mrb_state *mrb);
*/
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*);
+MRB_API mrb_value mrb_top_self(mrb_state *mrb);
+MRB_API mrb_value mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep);
+MRB_API mrb_value mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep);
+MRB_API mrb_value mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *iseq);
/* 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)
@@ -1069,27 +1232,25 @@ 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_obj_eq(mrb_state *mrb, mrb_value a, mrb_value b);
+MRB_API mrb_bool mrb_obj_equal(mrb_state *mrb, mrb_value a, mrb_value b);
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);
+#ifndef MRB_NO_FLOAT
+MRB_API mrb_value mrb_to_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);
+/* mrb_cmp(mrb, obj1, obj2): 1:0:-1; -2 for error */
+MRB_API mrb_int mrb_cmp(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_INLINE int
mrb_gc_arena_save(mrb_state *mrb)
{
return mrb->gc.arena_idx;
}
-static inline void
+MRB_INLINE void
mrb_gc_arena_restore(mrb_state *mrb, int idx)
{
mrb->gc.arena_idx = idx;
@@ -1108,12 +1269,15 @@ MRB_API void mrb_field_write_barrier(mrb_state *, struct RBasic*, struct RBasic*
} 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_type_convert(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method);
+#define mrb_convert_type(mrb, val, type, tname, method) mrb_type_convert(mrb, val, type, mrb_intern_lit(mrb, method))
+MRB_API mrb_value mrb_type_convert_check(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method);
+#define mrb_check_convert_type(mrb, val, type, tname, method) mrb_type_convert_check(mrb, val, type, mrb_intern_lit(mrb, 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);
@@ -1140,36 +1304,40 @@ 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 mrb_noreturn void mrb_frozen_error(mrb_state *mrb, void *frozen_obj);
+MRB_API mrb_noreturn void mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max);
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);
+/* function for `raisef` formatting */
+MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap);
/* 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"))
+#define MRB_ERROR_SYM(sym) mrb_intern_lit(mrb, #sym)
+#define E_RUNTIME_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(RuntimeError))
+#define E_TYPE_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(TypeError))
+#define E_ZERODIV_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(ZeroDivisionError))
+#define E_ARGUMENT_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(ArgumentError))
+#define E_INDEX_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(IndexError))
+#define E_RANGE_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(RangeError))
+#define E_NAME_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(NameError))
+#define E_NOMETHOD_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(NoMethodError))
+#define E_SCRIPT_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(ScriptError))
+#define E_SYNTAX_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(SyntaxError))
+#define E_LOCALJUMP_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(LocalJumpError))
+#define E_REGEXP_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(RegexpError))
+#define E_FROZEN_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(FrozenError))
+#define E_NOTIMP_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(NotImplementedError))
+#define E_KEY_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(KeyError))
+#ifndef MRB_NO_FLOAT
+# define E_FLOATDOMAIN_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(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);
@@ -1187,17 +1355,19 @@ MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj);
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))
+#define mrb_as_int(mrb, val) mrb_integer(mrb_to_int(mrb, val))
+
+/* string type checking (contrary to the name, it doesn't convert) */
+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_INLINE void mrb_check_frozen(mrb_state *mrb, void *o)
+{
+ if (mrb_frozen_p((struct RBasic*)o)) mrb_frozen_error(mrb, o);
+}
MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *c, const char *a, const char *b);
+MRB_API void mrb_define_alias_id(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym 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);
@@ -1207,34 +1377,36 @@ 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);
+/* obsolete function(s); will be removed */
+#define mrb_int(mrb, val) mrb_as_int(mrb, val)
-/*
+/**
* Resume a Fiber
*
- * @mrbgem mruby-fiber
+ * Implemented in 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
+ * Implemented in 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
+ * Implemented in mruby-fiber
*/
MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib);
-/*
+/**
* FiberError reference
*
- * @mrbgem mruby-fiber
+ * Implemented in mruby-fiber
*/
-#define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError"))
+#define E_FIBER_ERROR mrb_exc_get_id(mrb, MRB_ERROR_SYM(FiberError))
MRB_API void mrb_stack_extend(mrb_state*, mrb_int);
/* memory pool implementation */
@@ -1244,6 +1416,7 @@ 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);
+/* temporary memory allocation, only effective while GC arena is kept */
MRB_API void* mrb_alloca(mrb_state *mrb, size_t);
MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
@@ -1253,6 +1426,10 @@ MRB_API void mrb_show_copyright(mrb_state *mrb);
MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
+#ifdef MRB_PRESYM_SCANNING
+# include <mruby/presym/scanning.h>
+#endif
+
#if 0
/* memcpy and memset does not work with gdb reverse-next on my box */
/* use naive memcpy and memset instead */
diff --git a/include/mruby/array.h b/include/mruby/array.h
index 6fffe4512..105e825ea 100644
--- a/include/mruby/array.h
+++ b/include/mruby/array.h
@@ -1,5 +1,5 @@
-/*
-** mruby/array.h - Array class
+/**
+** @file mruby/array.h - Array class
**
** See Copyright Notice in mruby.h
*/
@@ -17,23 +17,32 @@ MRB_BEGIN_DECL
typedef struct mrb_shared_array {
int refcnt;
- mrb_int len;
+ mrb_ssize len;
mrb_value *ptr;
} mrb_shared_array;
-#define MRB_ARY_EMBED_LEN_MAX ((mrb_int)(sizeof(void*)*3/sizeof(mrb_value)))
+#if defined(MRB_32BIT) && defined(MRB_NO_BOXING) && !defined(MRB_USE_FLOAT32)
+# define MRB_ARY_NO_EMBED
+# define MRB_ARY_EMBED_LEN_MAX 0
+#else
+# define MRB_ARY_EMBED_LEN_MAX ((mrb_int)(sizeof(void*)*3/sizeof(mrb_value)))
+mrb_static_assert(MRB_ARY_EMBED_LEN_MAX > 0, "MRB_ARY_EMBED_LEN_MAX > 0");
+#endif
+
struct RArray {
MRB_OBJECT_HEADER;
union {
struct {
- mrb_int len;
+ mrb_ssize len;
union {
- mrb_int capa;
+ mrb_ssize capa;
mrb_shared_array *shared;
} aux;
mrb_value *ptr;
} heap;
- mrb_value embed[MRB_ARY_EMBED_LEN_MAX];
+#ifndef MRB_ARY_NO_EMBED
+ mrb_value ary[MRB_ARY_EMBED_LEN_MAX];
+#endif
} as;
};
@@ -41,14 +50,22 @@ struct RArray {
#define mrb_ary_value(p) mrb_obj_value((void*)(p))
#define RARRAY(v) ((struct RArray*)(mrb_ptr(v)))
+#ifdef MRB_ARY_NO_EMBED
+#define ARY_EMBED_P(a) 0
+#define ARY_UNSET_EMBED_FLAG(a) (void)0
+#define ARY_EMBED_LEN(a) 0
+#define ARY_SET_EMBED_LEN(a,len) (void)0
+#define ARY_EMBED_PTR(a) 0
+#else
#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_EMBED_PTR(a) ((a)->as.ary)
+#endif
-#define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len)
+#define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(mrb_int)(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))
@@ -90,7 +107,7 @@ MRB_API mrb_value mrb_ary_new(mrb_state *mrb);
* Array[value1, value2, ...]
*
* @param mrb The mruby state reference.
- * @param size The numer of values.
+ * @param size The number of values.
* @param vals The actual values.
* @return The initialized array.
*/
@@ -160,20 +177,6 @@ MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value 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:
@@ -183,7 +186,7 @@ MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
* @param mrb The mruby state reference.
* @param ary The target array.
* @param n The array index being referenced.
- * @param val The value being setted.
+ * @param val The value being set.
*/
MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
@@ -199,6 +202,7 @@ MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val
* @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);
/*
@@ -225,6 +229,24 @@ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item
* @param offset The element position (negative counts from the tail).
*/
MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
+#define mrb_ary_ref(mrb, ary, n) mrb_ary_entry(ary, n)
+
+/*
+ * Replace subsequence of an array.
+ *
+ * Equivalent to:
+ *
+ * ary[head, len] = rpl
+ *
+ * @param mrb The mruby state reference.
+ * @param self The array from which the value will be partiality replaced.
+ * @param head Beginning position of a replacement subsequence.
+ * @param len Length of a replacement subsequence.
+ * @param rpl The array of replacement elements.
+ * It is possible to pass `mrb_undef_value()` instead of an empty array.
+ * @return The receiver array.
+ */
+MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value self, mrb_int head, mrb_int len, mrb_value rpl);
/*
* Shifts the first element from the array.
@@ -261,7 +283,7 @@ MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self);
*
* @param mrb The mruby state reference.
* @param ary The target array
- * @param sep The separater, can be NULL
+ * @param sep The separator, can be NULL
*/
MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
@@ -274,6 +296,9 @@ MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
*/
MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len);
+/* helper functions */
+mrb_value mrb_ary_subseq(mrb_state *mrb, mrb_value ary, mrb_int beg, mrb_int len);
+
MRB_END_DECL
#endif /* MRUBY_ARRAY_H */
diff --git a/include/mruby/boxing_nan.h b/include/mruby/boxing_nan.h
index 3d9bbdc69..05f1d953c 100644
--- a/include/mruby/boxing_nan.h
+++ b/include/mruby/boxing_nan.h
@@ -1,5 +1,5 @@
-/*
-** mruby/boxing_nan.h - nan boxing mrb_value definition
+/**
+** @file mruby/boxing_nan.h - nan boxing mrb_value definition
**
** See Copyright Notice in mruby.h
*/
@@ -7,12 +7,12 @@
#ifndef MRUBY_BOXING_NAN_H
#define MRUBY_BOXING_NAN_H
-#ifdef MRB_USE_FLOAT
-# error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT conflict <<----
+#ifdef MRB_USE_FLOAT32
+# error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT32 conflict <<----
#endif
-#ifdef MRB_WITHOUT_FLOAT
-# error ---->> MRB_NAN_BOXING and MRB_WITHOUT_FLOAT conflict <<----
+#ifdef MRB_NO_FLOAT
+# error ---->> MRB_NAN_BOXING and MRB_NO_FLOAT conflict <<----
#endif
#ifdef MRB_INT64
@@ -20,13 +20,9 @@
#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
+#define MRB_SYMBOL_SHIFT 0
+#define MRB_FIXNUM_MIN INT32_MIN
+#define MRB_FIXNUM_MAX INT32_MAX
/* value representation by nan-boxing:
* float : FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF
@@ -37,66 +33,98 @@
* 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;
- };
+ uint64_t u;
} mrb_value;
-#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
+union mrb_value_ {
+ mrb_float f;
+ uint64_t u;
+#ifdef MRB_64BIT
+ void *p;
+# define BOXNAN_IMMEDIATE_VALUE uint32_t i
+#else
+# define BOXNAN_IMMEDIATE_VALUE union { uint32_t i; void *p; }
+#endif
+ struct {
+ MRB_ENDIAN_LOHI(
+ uint32_t ttt;
+ ,BOXNAN_IMMEDIATE_VALUE;
+ )
+ };
+ mrb_value value;
+};
+
+mrb_static_assert1(sizeof(mrb_value) == sizeof(union mrb_value_));
-#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
+static inline union mrb_value_
+mrb_val_union(mrb_value v)
+{
+ union mrb_value_ x;
+ x.value = v;
+ return x;
+}
+
+#define mrb_tt(o) ((enum mrb_vtype)((mrb_val_union(o).ttt & 0xfc000)>>14)-1)
+#define mrb_type(o) (enum mrb_vtype)((uint32_t)0xfff00000 < mrb_val_union(o).ttt ? mrb_tt(o) : MRB_TT_FLOAT)
+#define mrb_float(o) mrb_val_union(o).f
+#define mrb_fixnum(o) ((mrb_int)mrb_val_union(o).i)
+#define mrb_integer(o) mrb_fixnum(o)
+#define mrb_symbol(o) ((mrb_sym)mrb_val_union(o).i)
#ifdef MRB_64BIT
+#define mrb_ptr(o) ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)(mrb_val_union(o).p)))<<2))
+#define mrb_cptr(o) (((struct RCptr*)mrb_ptr(o))->p)
#define BOXNAN_SHIFT_LONG_POINTER(v) (((uintptr_t)(v)>>34)&0x3fff)
#else
+#define mrb_ptr(o) ((void*)mrb_val_union(o).i)
+#define mrb_cptr(o) mrb_ptr(o)
#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);\
+#define BOXNAN_SET_VALUE(o, tt, attr, v) do { \
+ union mrb_value_ mrb_value_union_variable; \
+ mrb_value_union_variable.attr = (v);\
+ mrb_value_union_variable.ttt = 0xfff00000 | (((tt)+1)<<14);\
+ o = mrb_value_union_variable.value;\
} while (0)
+#ifdef MRB_64BIT
#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));\
+ union mrb_value_ mrb_value_union_variable;\
+ mrb_value_union_variable.p = (void*)((uintptr_t)(v)>>2);\
+ mrb_value_union_variable.ttt = (0xfff00000|(((tt)+1)<<14)|BOXNAN_SHIFT_LONG_POINTER(v));\
+ o = mrb_value_union_variable.value;\
} while (0)
+#else
+#define BOXNAN_SET_OBJ_VALUE(o, tt, v) BOXNAN_SET_VALUE(o, tt, i, (uint32_t)v)
+#endif
#define SET_FLOAT_VALUE(mrb,r,v) do { \
- if (v != v) { \
- (r).value.ttt = 0x7ff80000; \
- (r).value.i = 0; \
+ union mrb_value_ mrb_value_union_variable; \
+ if ((v) != (v)) { /* NaN */ \
+ mrb_value_union_variable.ttt = 0x7ff80000; \
+ mrb_value_union_variable.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))
+ mrb_value_union_variable.f = (v); \
+ } \
+ r = mrb_value_union_variable.value; \
+} while(0)
+
+#define SET_NIL_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, i, 0)
+#define SET_FALSE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, i, 1)
+#define SET_TRUE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_TRUE, i, 1)
+#define SET_BOOL_VALUE(r,b) BOXNAN_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, i, 1)
+#define SET_INT_VALUE(mrb, r,n) BOXNAN_SET_VALUE(r, MRB_TT_INTEGER, i, (uint32_t)(n))
+#define SET_FIXNUM_VALUE(r,n) BOXNAN_SET_VALUE(r, MRB_TT_INTEGER, i, (uint32_t)(n))
+#define SET_SYM_VALUE(r,v) BOXNAN_SET_VALUE(r, MRB_TT_SYMBOL, i, (uint32_t)(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)
+#ifdef MRB_64BIT
+MRB_API mrb_value mrb_nan_boxing_cptr_value(struct mrb_state*, void*);
+#define SET_CPTR_VALUE(mrb,r,v) ((r) = mrb_nan_boxing_cptr_value(mrb, v))
+#else
+#define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_VALUE(r, MRB_TT_CPTR, p, v)
+#endif
+#define SET_UNDEF_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_UNDEF, i, 0)
#endif /* MRUBY_BOXING_NAN_H */
diff --git a/include/mruby/boxing_no.h b/include/mruby/boxing_no.h
index 86ce64555..84908a0d8 100644
--- a/include/mruby/boxing_no.h
+++ b/include/mruby/boxing_no.h
@@ -1,5 +1,5 @@
-/*
-** mruby/boxing_no.h - unboxed mrb_value definition
+/**
+** @file mruby/boxing_no.h - unboxed mrb_value definition
**
** See Copyright Notice in mruby.h
*/
@@ -8,30 +8,31 @@
#define MRUBY_BOXING_NO_H
#define MRB_FIXNUM_SHIFT 0
-#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
+#define MRB_SYMBOL_SHIFT 0
+#define MRB_FIXNUM_MIN MRB_INT_MIN
+#define MRB_FIXNUM_MAX MRB_INT_MAX
-typedef struct mrb_value {
- union {
-#ifndef MRB_WITHOUT_FLOAT
- mrb_float f;
+union mrb_value_union {
+#ifndef MRB_NO_FLOAT
+ mrb_float f;
#endif
- void *p;
- mrb_int i;
- mrb_sym sym;
- } value;
+ void *p;
+ mrb_int i;
+ mrb_sym sym;
+};
+
+typedef struct mrb_value {
+ union mrb_value_union 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
+#ifndef MRB_NO_FLOAT
#define mrb_float(o) (o).value.f
#endif
#define mrb_fixnum(o) (o).value.i
+#define mrb_integer(o) mrb_fixnum(o)
#define mrb_symbol(o) (o).value.sym
#define mrb_type(o) (o).tt
@@ -44,8 +45,9 @@ typedef struct mrb_value {
#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_INT_VALUE(mrb,r,n) BOXNIX_SET_VALUE(r, MRB_TT_INTEGER, value.i, (n))
+#define SET_FIXNUM_VALUE(r,n) BOXNIX_SET_VALUE(r, MRB_TT_INTEGER, value.i, (n))
+#ifndef MRB_NO_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))
diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h
index 2ff314144..beab8681e 100644
--- a/include/mruby/boxing_word.h
+++ b/include/mruby/boxing_word.h
@@ -1,5 +1,5 @@
-/*
-** mruby/boxing_word.h - word boxing mrb_value definition
+/**
+** @file mruby/boxing_word.h - word boxing mrb_value definition
**
** See Copyright Notice in mruby.h
*/
@@ -7,138 +7,199 @@
#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
+#ifndef MRB_NO_FLOAT
struct RFloat {
MRB_OBJECT_HEADER;
mrb_float f;
};
#endif
-struct RCptr {
+struct RInteger {
MRB_OBJECT_HEADER;
- void *p;
+ mrb_int i;
};
-#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,
+ MRB_Qnil = 0,
+ MRB_Qfalse = 4,
+ MRB_Qtrue = 12,
+ MRB_Qundef = 20,
};
-#define MRB_FIXNUM_FLAG 0x01
-#define MRB_SYMBOL_FLAG 0x0e
-#define MRB_SPECIAL_SHIFT 8
+#if defined(MRB_64BIT) && defined(MRB_INT32)
+#define MRB_FIXNUM_SHIFT 0
+#else
+#define MRB_FIXNUM_SHIFT BOXWORD_FIXNUM_SHIFT
+#endif
+#define MRB_SYMBOL_SHIFT BOXWORD_SYMBOL_SHIFT
-#if defined(MRB_64BIT)
-#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT)
-#define MRB_SYMBOL_MAX UINT32_MAX
+#if defined(MRB_64BIT) && defined(MRB_INT64)
+# define MRB_FIXNUM_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT)
+# define MRB_FIXNUM_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT)
#else
-#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT - MRB_SPECIAL_SHIFT)
-#define MRB_SYMBOL_MAX (UINT32_MAX >> MRB_SPECIAL_SHIFT)
+# define MRB_FIXNUM_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT)
+# define MRB_FIXNUM_MAX (INT32_MAX>>MRB_FIXNUM_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;
+#define BOXWORD_FIXNUM_BIT_POS 1
+#define BOXWORD_SYMBOL_BIT_POS 2
+#define BOXWORD_FIXNUM_SHIFT BOXWORD_FIXNUM_BIT_POS
+#ifdef MRB_64BIT
+#define BOXWORD_SYMBOL_SHIFT 0
+#else
+#define BOXWORD_SYMBOL_SHIFT BOXWORD_SYMBOL_BIT_POS
#endif
- struct RCptr *vp;
- } value;
- unsigned long w;
+#define BOXWORD_FIXNUM_FLAG (1 << (BOXWORD_FIXNUM_BIT_POS - 1))
+#define BOXWORD_SYMBOL_FLAG (1 << (BOXWORD_SYMBOL_BIT_POS - 1))
+#define BOXWORD_FIXNUM_MASK ((1 << BOXWORD_FIXNUM_BIT_POS) - 1)
+#define BOXWORD_SYMBOL_MASK ((1 << BOXWORD_SYMBOL_BIT_POS) - 1)
+#define BOXWORD_IMMEDIATE_MASK 0x07
+
+#define BOXWORD_SET_SHIFT_VALUE(o,n,v) \
+ ((o).w = (((uintptr_t)(v)) << BOXWORD_##n##_SHIFT) | BOXWORD_##n##_FLAG)
+#define BOXWORD_SHIFT_VALUE_P(o,n) \
+ (((o).w & BOXWORD_##n##_MASK) == BOXWORD_##n##_FLAG)
+#define BOXWORD_OBJ_TYPE_P(o,n) \
+ (!mrb_immediate_p(o) && mrb_val_union(o).bp->tt == MRB_TT_##n)
+
+/*
+ * mrb_value representation:
+ *
+ * nil : ...0000 0000 (all bits are 0)
+ * false : ...0000 0100 (mrb_fixnum(v) != 0)
+ * true : ...0000 1100
+ * undef : ...0001 0100
+ * fixnum: ...IIII III1
+ * symbol: ...SSSS SS10 (use only upper 32-bit as symbol value on 64-bit CPU)
+ * object: ...PPPP P000 (any bits are 1)
+ */
+typedef struct mrb_value {
+ uintptr_t w;
} mrb_value;
+union mrb_value_ {
+ void *p;
+#ifdef MRB_64BIT
+ /* use struct to avoid bit shift. */
+ struct {
+ MRB_ENDIAN_LOHI(
+ mrb_sym sym;
+ ,uint32_t flag;
+ )
+ } sym;
+#endif
+ struct RBasic *bp;
+#ifndef MRB_NO_FLOAT
+ struct RFloat *fp;
+#endif
+ struct RInteger *ip;
+ struct RCptr *vp;
+ uintptr_t w;
+ mrb_value value;
+};
+
+mrb_static_assert1(sizeof(mrb_value) == sizeof(union mrb_value_));
+
+static inline union mrb_value_
+mrb_val_union(mrb_value v)
+{
+ union mrb_value_ x;
+ x.value = v;
+ return x;
+}
+
MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*);
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_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
+MRB_API mrb_value mrb_word_boxing_int_value(struct mrb_state*, mrb_int);
-#ifndef MRB_WITHOUT_FLOAT
-#define mrb_float_pool(mrb,f) mrb_word_boxing_float_pool(mrb,f)
-#endif
+#define mrb_immediate_p(o) ((o).w & BOXWORD_IMMEDIATE_MASK || (o).w == MRB_Qnil)
-#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
+#define mrb_ptr(o) mrb_val_union(o).p
+#define mrb_cptr(o) mrb_val_union(o).vp->p
+#ifndef MRB_NO_FLOAT
+#define mrb_float(o) mrb_val_union(o).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_fixnum(o) (mrb_int)(((intptr_t)(o).w) >> BOXWORD_FIXNUM_SHIFT)
+MRB_INLINE mrb_int
+mrb_integer_func(mrb_value o) {
+ if (mrb_immediate_p(o)) return mrb_fixnum(o);
+ return mrb_val_union(o).ip->i;
}
+#define mrb_integer(o) mrb_integer_func(o)
+#ifdef MRB_64BIT
+#define mrb_symbol(o) mrb_val_union(o).sym.sym
+#else
+#define mrb_symbol(o) (mrb_sym)(((o).w) >> BOXWORD_SYMBOL_SHIFT)
+#endif
+#define mrb_bool(o) (((o).w & ~(uintptr_t)MRB_Qfalse) != 0)
-#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_fixnum_p(o) BOXWORD_SHIFT_VALUE_P(o, FIXNUM)
+#define mrb_integer_p(o) (BOXWORD_SHIFT_VALUE_P(o, FIXNUM)||BOXWORD_OBJ_TYPE_P(o, INTEGER))
+#ifdef MRB_64BIT
+#define mrb_symbol_p(o) (mrb_val_union(o).sym.flag == BOXWORD_SYMBOL_FLAG)
+#else
+#define mrb_symbol_p(o) BOXWORD_SHIFT_VALUE_P(o, SYMBOL)
+#endif
#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;\
- }\
+#define mrb_false_p(o) ((o).w == MRB_Qfalse)
+#define mrb_true_p(o) ((o).w == MRB_Qtrue)
+#ifndef MRB_NO_FLOAT
+#define mrb_float_p(o) BOXWORD_OBJ_TYPE_P(o, FLOAT)
+#endif
+#define mrb_array_p(o) BOXWORD_OBJ_TYPE_P(o, ARRAY)
+#define mrb_string_p(o) BOXWORD_OBJ_TYPE_P(o, STRING)
+#define mrb_hash_p(o) BOXWORD_OBJ_TYPE_P(o, HASH)
+#define mrb_cptr_p(o) BOXWORD_OBJ_TYPE_P(o, CPTR)
+#define mrb_exception_p(o) BOXWORD_OBJ_TYPE_P(o, EXCEPTION)
+#define mrb_free_p(o) BOXWORD_OBJ_TYPE_P(o, FREE)
+#define mrb_object_p(o) BOXWORD_OBJ_TYPE_P(o, OBJECT)
+#define mrb_class_p(o) BOXWORD_OBJ_TYPE_P(o, CLASS)
+#define mrb_module_p(o) BOXWORD_OBJ_TYPE_P(o, MODULE)
+#define mrb_iclass_p(o) BOXWORD_OBJ_TYPE_P(o, ICLASS)
+#define mrb_sclass_p(o) BOXWORD_OBJ_TYPE_P(o, SCLASS)
+#define mrb_proc_p(o) BOXWORD_OBJ_TYPE_P(o, PROC)
+#define mrb_range_p(o) BOXWORD_OBJ_TYPE_P(o, RANGE)
+#define mrb_env_p(o) BOXWORD_OBJ_TYPE_P(o, ENV)
+#define mrb_data_p(o) BOXWORD_OBJ_TYPE_P(o, DATA)
+#define mrb_fiber_p(o) BOXWORD_OBJ_TYPE_P(o, FIBER)
+#define mrb_istruct_p(o) BOXWORD_OBJ_TYPE_P(o, ISTRUCT)
+#define mrb_break_p(o) BOXWORD_OBJ_TYPE_P(o, BREAK)
+
+#ifndef MRB_NO_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_UNDEF_VALUE(r) ((r).w = MRB_Qundef)
+#define SET_NIL_VALUE(r) ((r).w = MRB_Qnil)
+#define SET_FALSE_VALUE(r) ((r).w = MRB_Qfalse)
+#define SET_TRUE_VALUE(r) ((r).w = MRB_Qtrue)
+#define SET_BOOL_VALUE(r,b) ((b) ? SET_TRUE_VALUE(r) : SET_FALSE_VALUE(r))
+#define SET_INT_VALUE(mrb,r,n) ((r) = mrb_word_boxing_int_value(mrb, n))
+#define SET_FIXNUM_VALUE(r,n) BOXWORD_SET_SHIFT_VALUE(r, FIXNUM, n)
+#ifdef MRB_64BIT
+#define SET_SYM_VALUE(r,v) do {\
+ union mrb_value_ mrb_value_union_variable;\
+ mrb_value_union_variable.sym.sym = v;\
+ mrb_value_union_variable.sym.flag = BOXWORD_SYMBOL_FLAG;\
+ (r) = mrb_value_union_variable.value;\
} while (0)
-
-#ifndef MRB_WITHOUT_FLOAT
-#define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v)
+#else
+#define SET_SYM_VALUE(r,n) BOXWORD_SET_SHIFT_VALUE(r, SYMBOL, n)
#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)
+#define SET_OBJ_VALUE(r,v) ((r).w = (uintptr_t)(v))
+
+MRB_INLINE enum mrb_vtype
+mrb_type(mrb_value o)
+{
+ return !mrb_bool(o) ? MRB_TT_FALSE :
+ mrb_true_p(o) ? MRB_TT_TRUE :
+ mrb_fixnum_p(o) ? MRB_TT_INTEGER :
+ mrb_symbol_p(o) ? MRB_TT_SYMBOL :
+ mrb_undef_p(o) ? MRB_TT_UNDEF :
+ mrb_val_union(o).bp->tt;
+}
#endif /* MRUBY_BOXING_WORD_H */
diff --git a/include/mruby/class.h b/include/mruby/class.h
index 97b1df58a..b97bc3991 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -1,5 +1,5 @@
-/*
-** mruby/class.h - Class class
+/**
+** @file mruby/class.h - Class class
**
** See Copyright Notice in mruby.h
*/
@@ -17,13 +17,13 @@ MRB_BEGIN_DECL
struct RClass {
MRB_OBJECT_HEADER;
struct iv_tbl *iv;
- struct kh_mt *mt;
+ struct mt_tbl *mt;
struct RClass *super;
};
#define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v)))
-static inline struct RClass*
+MRB_INLINE struct RClass*
mrb_class(mrb_state *mrb, mrb_value v)
{
switch (mrb_type(v)) {
@@ -35,9 +35,9 @@ mrb_class(mrb_state *mrb, mrb_value v)
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_INTEGER:
+ return mrb->integer_class;
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return mrb->float_class;
#endif
@@ -61,37 +61,48 @@ mrb_class(mrb_state *mrb, mrb_value v)
#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;\
+ 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)
+#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);
+struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
+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 void mrb_remove_method(mrb_state *mrb, struct RClass *c, mrb_sym sym);
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);
+mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv);
void mrb_class_name_class(mrb_state*, struct RClass*, struct RClass*, mrb_sym);
+mrb_bool mrb_const_name_p(mrb_state*, const char*, mrb_int);
mrb_value mrb_class_find_path(mrb_state*, struct RClass*);
+mrb_value mrb_mod_to_s(mrb_state*, mrb_value);
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*);
+#ifndef MRB_NO_METHOD_CACHE
+void mrb_mc_clear_by_class(mrb_state *mrb, struct RClass* c);
+#else
+#define mrb_mc_clear_by_class(mrb,c)
+#endif
+
+/* return non zero to break the loop */
+typedef int (mrb_mt_foreach_func)(mrb_state*,mrb_sym,mrb_method_t,void*);
+MRB_API void mrb_mt_foreach(mrb_state*, struct RClass*, mrb_mt_foreach_func*, void*);
+
MRB_END_DECL
#endif /* MRUBY_CLASS_H */
diff --git a/include/mruby/common.h b/include/mruby/common.h
index 4c7d9384a..90c97cf65 100644
--- a/include/mruby/common.h
+++ b/include/mruby/common.h
@@ -1,5 +1,5 @@
-/*
-**"common.h - mruby common platform definition"
+/**
+** @file common.h - mruby common platform definition"
**
** See Copyright Notice in mruby.h
*/
@@ -14,7 +14,7 @@
#endif
#ifdef __cplusplus
-#ifdef MRB_ENABLE_CXX_ABI
+#ifdef MRB_USE_CXX_ABI
#define MRB_BEGIN_DECL
#define MRB_END_DECL
#else
@@ -34,7 +34,7 @@
MRB_BEGIN_DECL
/** Declare a function that never returns. */
-#if __STDC_VERSION__ >= 201112L
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L
# define mrb_noreturn _Noreturn
#elif defined __GNUC__ && !defined __STRICT_ANSI__
# define mrb_noreturn __attribute__((noreturn))
@@ -54,14 +54,15 @@ MRB_BEGIN_DECL
#endif
/** Declare a function as always inlined. */
-#if defined(_MSC_VER)
-# define MRB_INLINE static __inline
-#else
-# define MRB_INLINE static inline
+#if defined _MSC_VER && _MSC_VER < 1900
+# ifndef __cplusplus
+# define inline __inline
+# endif
#endif
-
+#define MRB_INLINE static inline
/** Declare a public MRuby API function. */
+#ifndef MRB_API
#if defined(MRB_BUILD_AS_DLL)
#if defined(MRB_CORE) || defined(MRB_LIB)
# define MRB_API __declspec(dllexport)
@@ -71,6 +72,20 @@ MRB_BEGIN_DECL
#else
# define MRB_API extern
#endif
+#endif
+
+/** Declare mingw versions */
+#if defined(__MINGW32__) || defined(__MINGW64__)
+# include <_mingw.h>
+# if defined(__MINGW64_VERSION_MAJOR)
+# define MRB_MINGW64_VERSION (__MINGW64_VERSION_MAJOR * 1000 + __MINGW64_VERSION_MINOR)
+# elif defined(__MINGW32_MAJOR_VERSION)
+# define MRB_MINGW32_VERSION (__MINGW32_MAJOR_VERSION * 1000 + __MINGW32_MINOR_VERSION)
+# endif
+# if defined(__MINGW32__) && !defined(__MINGW64__)
+# define MRB_MINGW32_LEGACY
+# endif
+#endif
MRB_END_DECL
diff --git a/include/mruby/compile.h b/include/mruby/compile.h
index a85460415..8159cd696 100644
--- a/include/mruby/compile.h
+++ b/include/mruby/compile.h
@@ -1,5 +1,5 @@
-/*
-** mruby/compile.h - mruby parser
+/**
+** @file mruby/compile.h - mruby parser
**
** See Copyright Notice in mruby.h
*/
@@ -24,7 +24,7 @@ typedef struct mrbc_context {
mrb_sym *syms;
int slen;
char *filename;
- short lineno;
+ uint16_t lineno;
int (*partial_hook)(struct mrb_parser_state*);
void *partial_data;
struct RClass *target_class;
@@ -33,7 +33,7 @@ typedef struct mrbc_context {
mrb_bool no_exec:1;
mrb_bool keep_lv:1;
mrb_bool no_optimize:1;
- mrb_bool on_eval:1;
+ const struct RProc *upper;
size_t parser_nerr;
} mrbc_context;
@@ -42,6 +42,7 @@ 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);
+MRB_API void mrbc_cleanup_local_variables(mrb_state *mrb, mrbc_context *c);
/* AST node structure */
typedef struct mrb_ast_node {
@@ -67,7 +68,7 @@ enum mrb_lex_state_enum {
/* saved error message */
struct mrb_parser_message {
- int lineno;
+ uint16_t lineno;
int column;
char* message;
};
@@ -98,6 +99,9 @@ enum mrb_string_type {
/* heredoc structure */
struct mrb_parser_heredoc_info {
mrb_bool allow_indent:1;
+ mrb_bool remove_indent:1;
+ size_t indent;
+ mrb_ast_node *indented;
mrb_bool line_head:1;
enum mrb_string_type type;
const char *term;
@@ -105,7 +109,7 @@ struct mrb_parser_heredoc_info {
mrb_ast_node *doc;
};
-#define MRB_PARSER_TOKBUF_MAX 65536
+#define MRB_PARSER_TOKBUF_MAX (UINT16_MAX-1)
#define MRB_PARSER_TOKBUF_SIZE 256
/* parser structure */
@@ -114,12 +118,13 @@ struct mrb_parser_state {
struct mrb_pool *pool;
mrb_ast_node *cells;
const char *s, *send;
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
+ /* If both f and s are non-null, it will be taken preferentially from s until s < send. */
FILE *f;
#endif
mrbc_context *cxt;
- char const *filename;
- int lineno;
+ mrb_sym filename_sym;
+ uint16_t lineno;
int column;
enum mrb_lex_state_enum lstate;
@@ -143,7 +148,6 @@ struct mrb_parser_state {
mrb_ast_node *heredocs_from_nextline;
mrb_ast_node *parsing_heredoc;
mrb_ast_node *lex_strterm_before_heredoc;
- mrb_bool heredoc_end_now:1; /* for mirb */
void *ylval;
@@ -152,16 +156,17 @@ struct mrb_parser_state {
mrb_ast_node *tree;
mrb_bool no_optimize:1;
- mrb_bool on_eval:1;
mrb_bool capture_errors:1;
+ const struct RProc *upper;
struct mrb_parser_message error_buffer[10];
struct mrb_parser_message warn_buffer[10];
mrb_sym* filename_table;
- size_t filename_table_length;
- int current_filename_index;
+ uint16_t filename_table_length;
+ uint16_t current_filename_index;
struct mrb_jmpbuf* jmp;
+ mrb_ast_node *nvars;
};
MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*);
@@ -169,10 +174,10 @@ 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);
+MRB_API mrb_sym mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
/* utility functions */
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_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*);
@@ -180,10 +185,19 @@ MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,size_t
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
+/** program load functions
+* Please note! Currently due to interactions with the GC calling these functions will
+* leak one RProc object per function call.
+* To prevent this save the current memory arena before calling and restore the arena
+* right after, like so
+* int ai = mrb_gc_arena_save(mrb);
+* mrb_value status = mrb_load_string(mrb, buffer);
+* mrb_gc_arena_restore(mrb, ai);
+*/
+#ifndef MRB_NO_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);
+MRB_API mrb_value mrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, mrbc_context *c);
#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);
diff --git a/include/mruby/data.h b/include/mruby/data.h
index 590470528..7bdf1c34e 100644
--- a/include/mruby/data.h
+++ b/include/mruby/data.h
@@ -1,5 +1,5 @@
-/*
-** mruby/data.h - Data class
+/**
+** @file mruby/data.h - Data class
**
** See Copyright Notice in mruby.h
*/
@@ -39,10 +39,11 @@ MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass
#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);\
+#define Data_Make_Struct(mrb,klass,strct,type,sval,data_obj) do { \
+ (data_obj) = Data_Wrap_Struct(mrb,klass,type,NULL);\
+ (sval) = (strct *)mrb_malloc(mrb, sizeof(strct)); \
+ { static const strct zero = { 0 }; *(sval) = zero; };\
+ (data_obj)->data = (sval);\
} while (0)
#define RDATA(obj) ((struct RData *)(mrb_ptr(obj)))
@@ -62,10 +63,10 @@ MRB_API void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_t
*(void**)&sval = mrb_data_get_ptr(mrb, obj, type); \
} while (0)
-static inline void
+MRB_INLINE void
mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type)
{
- mrb_assert(mrb_type(v) == MRB_TT_DATA);
+ mrb_assert(mrb_data_p(v));
DATA_PTR(v) = ptr;
DATA_TYPE(v) = type;
}
diff --git a/include/mruby/debug.h b/include/mruby/debug.h
index d1de34882..7319ccb3f 100644
--- a/include/mruby/debug.h
+++ b/include/mruby/debug.h
@@ -1,5 +1,5 @@
-/*
-** mruby/debug.h - mruby debug info
+/**
+** @file mruby/debug.h - mruby debug info
**
** See Copyright Notice in mruby.h
*/
@@ -16,7 +16,8 @@ MRB_BEGIN_DECL
typedef enum mrb_debug_line_type {
mrb_debug_line_ary = 0,
- mrb_debug_line_flat_map = 1
+ mrb_debug_line_flat_map,
+ mrb_debug_line_packed_map
} mrb_debug_line_type;
typedef struct mrb_irep_debug_info_line {
@@ -26,14 +27,14 @@ typedef struct 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;
+ mrb_irep_debug_info_line *flat_map;
+ uint8_t *packed_map;
} lines;
} mrb_irep_debug_info_file;
@@ -47,18 +48,19 @@ typedef struct 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);
+MRB_API const char *mrb_debug_get_filename(mrb_state *mrb, const mrb_irep *irep, uint32_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 int32_t mrb_debug_get_line(mrb_state *mrb, const mrb_irep *irep, uint32_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 *irep,
+ mrb_state *mrb, mrb_irep_debug_info *info,
+ const char *filename, uint16_t *lines,
uint32_t start_pos, uint32_t end_pos);
-MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep);
MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d);
MRB_END_DECL
diff --git a/include/mruby/dump.h b/include/mruby/dump.h
index bff838484..3c48866d9 100644
--- a/include/mruby/dump.h
+++ b/include/mruby/dump.h
@@ -1,5 +1,5 @@
-/*
-** mruby/dump.h - mruby binary dumper (mrbc binary format)
+/**
+** @file mruby/dump.h - mruby binary dumper (mrbc binary format)
**
** See Copyright Notice in mruby.h
*/
@@ -16,21 +16,22 @@
*/
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
+/* flags for mrb_dump_irep{,_binary,_cfunc,_cstruct} */
+#define MRB_DUMP_DEBUG_INFO 1
+#define MRB_DUMP_STATIC 2
+#define DUMP_DEBUG_INFO MRB_DUMP_DEBUG_INFO /* deprecated */
-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);
+int mrb_dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size);
+#ifndef MRB_NO_STDIO
+int mrb_dump_irep_binary(mrb_state*, const mrb_irep*, uint8_t, FILE*);
+int mrb_dump_irep_cfunc(mrb_state *mrb, const mrb_irep*, uint8_t flags, FILE *f, const char *initname);
+int mrb_dump_irep_cstruct(mrb_state *mrb, const 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*);
+MRB_API mrb_irep *mrb_read_irep_buf(mrb_state*, const void*, size_t);
/* dump/load error code
*
@@ -41,26 +42,28 @@ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
#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)
+#define MRB_DUMP_INVALID_FILE_HEADER (-4)
+#define MRB_DUMP_INVALID_IREP (-5)
+#define MRB_DUMP_INVALID_ARGUMENT (-6)
/* 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"
+/* Binary Format Version Major:Minor */
+/* Major: Incompatible to prior versions */
+/* Minor: Upper-compatible to prior versions */
+#define RITE_BINARY_MAJOR_VER "02"
+#define RITE_BINARY_MINOR_VER "00"
+#define RITE_BINARY_FORMAT_VER RITE_BINARY_MAJOR_VER RITE_BINARY_MINOR_VER
#define RITE_COMPILER_NAME "MATZ"
#define RITE_COMPILER_VERSION "0000"
-#define RITE_VM_VER "0002"
+#define RITE_VM_VER "0300"
#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"
@@ -70,8 +73,8 @@ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_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 major_version[2]; /* Binary Format Major Version */
+ uint8_t minor_version[2]; /* Binary Format Minor Version */
uint8_t binary_size[4]; /* Binary Size */
uint8_t compiler_name[4]; /* Compiler name */
uint8_t compiler_version[4];
@@ -92,10 +95,6 @@ struct rite_section_irep_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;
};
@@ -110,17 +109,6 @@ 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)
{
@@ -146,16 +134,6 @@ uint32_to_bin(uint32_t l, uint8_t *bin)
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)
{
@@ -165,15 +143,6 @@ bin_to_uint32(const uint8_t *bin)
(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)
{
@@ -189,8 +158,4 @@ bin_to_uint8(const uint8_t *bin)
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 */
diff --git a/include/mruby/endian.h b/include/mruby/endian.h
new file mode 100644
index 000000000..477f3bc94
--- /dev/null
+++ b/include/mruby/endian.h
@@ -0,0 +1,44 @@
+/**
+** @file mruby/endian.h - detect endian-ness
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_ENDIAN_H
+#define MRUBY_ENDIAN_H
+
+#include <limits.h>
+
+MRB_BEGIN_DECL
+
+#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER__)
+# define BYTE_ORDER __BYTE_ORDER__
+#endif
+#if !defined(BIG_ENDIAN) && defined(__ORDER_BIG_ENDIAN__)
+# define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+#endif
+#if !defined(LITTLE_ENDIAN) && defined(__ORDER_LITTLE_ENDIAN__)
+# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+#endif
+
+#ifdef BYTE_ORDER
+# if BYTE_ORDER == BIG_ENDIAN
+# define littleendian 0
+# elif BYTE_ORDER == LITTLE_ENDIAN
+# define littleendian 1
+# endif
+#endif
+#ifndef littleendian
+/* can't distinguish endian in compile time */
+static inline int
+check_little_endian(void)
+{
+ unsigned int n = 1;
+ return (*(unsigned char *)&n == 1);
+}
+# define littleendian check_little_endian()
+#endif
+
+MRB_END_DECL
+
+#endif /* MRUBY_ENDIAN_H */
diff --git a/include/mruby/error.h b/include/mruby/error.h
index 1587795fc..20ca38586 100644
--- a/include/mruby/error.h
+++ b/include/mruby/error.h
@@ -1,5 +1,5 @@
-/*
-** mruby/error.h - Exception class
+/**
+** @file mruby/error.h - Exception class
**
** See Copyright Notice in mruby.h
*/
@@ -23,32 +23,102 @@ struct RException {
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))
+#define mrb_exc_new_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit))
+#define mrb_exc_new_str_lit(mrb, c, lit) mrb_exc_new_lit(mrb, c, 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_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc);
+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 */
+/* declaration for `fail` method */
MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
+#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT32) || defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING)
+struct RBreak {
+ MRB_OBJECT_HEADER;
+ const struct RProc *proc;
+ mrb_value val;
+};
+#define mrb_break_value_get(brk) ((brk)->val)
+#define mrb_break_value_set(brk, v) ((brk)->val = v)
+#else
struct RBreak {
MRB_OBJECT_HEADER;
- struct RProc *proc;
+ const struct RProc *proc;
+ union mrb_value_union value;
+};
+#define RBREAK_VALUE_TT_MASK ((1 << 8) - 1)
+static inline mrb_value
+mrb_break_value_get(struct RBreak *brk)
+{
mrb_value val;
+ val.value = brk->value;
+ val.tt = (enum mrb_vtype)(brk->flags & RBREAK_VALUE_TT_MASK);
+ return val;
+}
+static inline void
+mrb_break_value_set(struct RBreak *brk, mrb_value val)
+{
+ brk->value = val.value;
+ brk->flags &= ~RBREAK_VALUE_TT_MASK;
+ brk->flags |= val.tt;
+}
+#endif /* MRB_64BIT || MRB_USE_FLOAT32 || MRB_NAN_BOXING || MRB_WORD_BOXING */
+#define mrb_break_proc_get(brk) ((brk)->proc)
+#define mrb_break_proc_set(brk, p) ((brk)->proc = p)
+
+#define RBREAK_TAG_FOREACH(f) \
+ f(RBREAK_TAG_BREAK, 0) \
+ f(RBREAK_TAG_BREAK_UPPER, 1) \
+ f(RBREAK_TAG_BREAK_INTARGET, 2) \
+ f(RBREAK_TAG_RETURN_BLOCK, 3) \
+ f(RBREAK_TAG_RETURN, 4) \
+ f(RBREAK_TAG_RETURN_TOPLEVEL, 5) \
+ f(RBREAK_TAG_JUMP, 6) \
+ f(RBREAK_TAG_STOP, 7)
+
+#define RBREAK_TAG_DEFINE(tag, i) tag = i,
+enum {
+ RBREAK_TAG_FOREACH(RBREAK_TAG_DEFINE)
};
+#undef RBREAK_TAG_DEFINE
+
+#define RBREAK_TAG_BIT 3
+#define RBREAK_TAG_BIT_OFF 8
+#define RBREAK_TAG_MASK (~(~UINT32_C(0) << RBREAK_TAG_BIT))
+
+static inline uint32_t
+mrb_break_tag_get(struct RBreak *brk)
+{
+ return (brk->flags >> RBREAK_TAG_BIT_OFF) & RBREAK_TAG_MASK;
+}
+
+static inline void
+mrb_break_tag_set(struct RBreak *brk, uint32_t tag)
+{
+ brk->flags &= ~(RBREAK_TAG_MASK << RBREAK_TAG_BIT_OFF);
+ brk->flags |= (tag & RBREAK_TAG_MASK) << RBREAK_TAG_BIT_OFF;
+}
/**
* Protect
*
- * @mrbgem mruby-error
+ */
+typedef mrb_value mrb_protect_error_func(mrb_state *mrb, void *userdata);
+MRB_API mrb_value mrb_protect_error(mrb_state *mrb, mrb_protect_error_func *body, void *userdata, mrb_bool *error);
+
+/**
+ * Protect (takes mrb_value for body argument)
+ *
+ * Implemented in the mruby-error mrbgem
*/
MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state);
/**
* Ensure
*
- * @mrbgem mruby-error
+ * Implemented in the mruby-error mrbgem
*/
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);
@@ -56,7 +126,7 @@ MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
/**
* Rescue
*
- * @mrbgem mruby-error
+ * Implemented in the mruby-error mrbgem
*/
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);
@@ -64,7 +134,7 @@ MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
/**
* Rescue exception
*
- * @mrbgem mruby-error
+ * Implemented in the mruby-error mrbgem
*/
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,
diff --git a/include/mruby/gc.h b/include/mruby/gc.h
index 2a3ff4182..9f9bba410 100644
--- a/include/mruby/gc.h
+++ b/include/mruby/gc.h
@@ -1,5 +1,5 @@
-/*
-** mruby/gc.h - garbage collector for mruby
+/**
+** @file mruby/gc.h - garbage collector for mruby
**
** See Copyright Notice in mruby.h
*/
@@ -21,6 +21,7 @@ struct mrb_state;
#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);
+size_t mrb_objspace_page_slot_size(void);
MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c);
#ifndef MRB_GC_ARENA_SIZE
@@ -86,6 +87,8 @@ typedef struct mrb_gc {
MRB_API mrb_bool
mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object);
+#define MRB_GC_RED 7
+
MRB_END_DECL
#endif /* MRUBY_GC_H */
diff --git a/include/mruby/hash.h b/include/mruby/hash.h
index dacdd9376..749ea3869 100644
--- a/include/mruby/hash.h
+++ b/include/mruby/hash.h
@@ -1,5 +1,5 @@
-/*
-** mruby/hash.h - Hash class
+/**
+** @file mruby/hash.h - Hash class
**
** See Copyright Notice in mruby.h
*/
@@ -8,23 +8,35 @@
#define MRUBY_HASH_H
#include "common.h"
-#include <mruby/khash.h>
/**
* Hash class
*/
MRB_BEGIN_DECL
+/* offset of `iv` must be 3 words */
struct RHash {
MRB_OBJECT_HEADER;
+#ifdef MRB_64BIT
+ uint32_t size;
struct iv_tbl *iv;
- struct seglist *ht;
+ uint32_t ea_capa;
+ uint32_t ea_n_used;
+#else
+ struct iv_tbl *iv;
+ uint32_t size;
+#endif
+ union {
+ struct hash_entry *ea;
+ struct hash_table *ht;
+ } hsh;
};
#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);
+size_t mrb_hash_memsize(mrb_value obj);
+MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, mrb_int capa);
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);
@@ -96,7 +108,7 @@ MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key,
* @param mrb The mruby state reference.
* @param hash The target hash.
* @param key The key to delete.
- * @return The deleted value.
+ * @return The deleted value. This value is not protected from GC. Use `mrb_gc_protect()` if necessary.
*/
MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
@@ -166,8 +178,22 @@ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash);
MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
/*
- * Copies the hash.
+ * Get hash size.
+ *
+ * Equivalent to:
*
+ * hash.size
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return The hash size.
+ */
+MRB_API mrb_int mrb_hash_size(mrb_state *mrb, mrb_value hash);
+
+/*
+ * Copies the hash. This function does NOT copy the instance variables
+ * (except for the default value). Use mrb_obj_dup() to copy the instance
+ * variables as well.
*
* @param mrb The mruby state reference.
* @param hash The target hash.
@@ -185,31 +211,32 @@ MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value 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)
+#define RHASH(hash) ((struct RHash*)(mrb_ptr(hash)))
+
+#define MRB_HASH_IB_BIT_BIT 5
+#define MRB_HASH_AR_EA_CAPA_BIT 5
+#define MRB_HASH_IB_BIT_SHIFT 0
+#define MRB_HASH_AR_EA_CAPA_SHIFT 0
+#define MRB_HASH_AR_EA_N_USED_SHIFT MRB_HASH_AR_EA_CAPA_BIT
+#define MRB_HASH_SIZE_FLAGS_SHIFT (MRB_HASH_AR_EA_CAPA_BIT * 2)
+#define MRB_HASH_IB_BIT_MASK ((1 << MRB_HASH_IB_BIT_BIT) - 1)
+#define MRB_HASH_AR_EA_CAPA_MASK ((1 << MRB_HASH_AR_EA_CAPA_BIT) - 1)
+#define MRB_HASH_AR_EA_N_USED_MASK (MRB_HASH_AR_EA_CAPA_MASK << MRB_HASH_AR_EA_N_USED_SHIFT)
+#define MRB_HASH_DEFAULT (1 << (MRB_HASH_SIZE_FLAGS_SHIFT + 0))
+#define MRB_HASH_PROC_DEFAULT (1 << (MRB_HASH_SIZE_FLAGS_SHIFT + 1))
+#define MRB_HASH_HT (1 << (MRB_HASH_SIZE_FLAGS_SHIFT + 2))
+#define MRB_RHASH_DEFAULT_P(hash) (RHASH(hash)->flags & MRB_HASH_DEFAULT)
+#define MRB_RHASH_PROCDEFAULT_P(hash) (RHASH(hash)->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*);
+/* return non zero to break the loop */
+typedef int (mrb_hash_foreach_func)(mrb_state *mrb, mrb_value key, mrb_value val, void *data);
+MRB_API void mrb_hash_foreach(mrb_state *mrb, struct RHash *hash, mrb_hash_foreach_func *func, void *p);
+
MRB_END_DECL
#endif /* MRUBY_HASH_H */
diff --git a/include/mruby/irep.h b/include/mruby/irep.h
index 76bafb25c..6a3b4bd10 100644
--- a/include/mruby/irep.h
+++ b/include/mruby/irep.h
@@ -1,5 +1,5 @@
-/*
-** mruby/irep.h - mrb_irep structure
+/**
+** @file mruby/irep.h - mrb_irep structure
**
** See Copyright Notice in mruby.h
*/
@@ -16,43 +16,102 @@
MRB_BEGIN_DECL
enum irep_pool_type {
- IREP_TT_STRING,
- IREP_TT_FIXNUM,
- IREP_TT_FLOAT,
+ IREP_TT_STR = 0, /* string (need free) */
+ IREP_TT_SSTR = 2, /* string (static) */
+ IREP_TT_INT32 = 1, /* 32bit integer */
+ IREP_TT_INT64 = 3, /* 64bit integer */
+ IREP_TT_BIGINT = 7, /* big integer (not yet supported) */
+ IREP_TT_FLOAT = 5, /* float (double/float) */
+};
+
+#define IREP_TT_NFLAG 1 /* number (non string) flag */
+#define IREP_TT_SFLAG 2 /* static string flag */
+
+typedef struct mrb_pool_value {
+ uint32_t tt; /* packed type and length (for string) */
+ union {
+ const char *str;
+ int32_t i32;
+ int64_t i64;
+#ifndef MRB_NO_FLOAT
+ mrb_float f;
+#endif
+ } u;
+} mrb_pool_value;
+
+enum mrb_catch_type {
+ MRB_CATCH_RESCUE = 0,
+ MRB_CATCH_ENSURE = 1,
};
-struct mrb_locals {
- mrb_sym name;
- uint16_t r;
+struct mrb_irep_catch_handler {
+ uint8_t type; /* enum mrb_catch_type */
+ uint8_t begin[4]; /* The starting address to match the handler. Includes this. */
+ uint8_t end[4]; /* The endpoint address that matches the handler. Not Includes this. */
+ uint8_t target[4]; /* The address to jump to if a match is made. */
};
/* Program data array struct */
typedef struct mrb_irep {
uint16_t nlocals; /* Number of local variables */
uint16_t nregs; /* Number of register variables */
+ uint16_t clen; /* Number of catch handlers */
uint8_t flags;
- mrb_code *iseq;
- mrb_value *pool;
- mrb_sym *syms;
- struct mrb_irep **reps;
+ const mrb_code *iseq;
+ /*
+ * A catch handler table is placed after the iseq entity.
+ * The reason it doesn't add fields to the structure is to keep the mrb_irep structure from bloating.
+ * The catch handler table can be obtained with `mrb_irep_catch_handler_table(irep)`.
+ */
+ const mrb_pool_value *pool;
+ const mrb_sym *syms;
+ const struct mrb_irep * const *reps;
- struct mrb_locals *lv;
+ const mrb_sym *lv;
/* debug info */
- mrb_bool own_filename;
- const char *filename;
- uint16_t *lines;
struct mrb_irep_debug_info* debug_info;
- uint16_t ilen, plen, slen, rlen;
- uint32_t refcnt;
+ uint32_t ilen;
+ uint16_t plen, slen;
+ uint16_t rlen;
+ uint16_t refcnt;
} mrb_irep;
#define MRB_ISEQ_NO_FREE 1
+#define MRB_IREP_NO_FREE 2
+#define MRB_IREP_STATIC (MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE)
MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb);
+
+/** load mruby bytecode functions
+* Please note! Currently due to interactions with the GC calling these functions will
+* leak one RProc object per function call.
+* To prevent this save the current memory arena before calling and restore the arena
+* right after, like so
+* int ai = mrb_gc_arena_save(mrb);
+* mrb_value status = mrb_load_irep(mrb, buffer);
+* mrb_gc_arena_restore(mrb, ai);
+*/
+
+/* @param [const uint8_t*] irep code, expected as a literal */
MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*);
+
+/*
+ * @param [const void*] irep code
+ * @param [size_t] size of irep buffer. If -1 is given, it is considered unrestricted.
+ */
+MRB_API mrb_value mrb_load_irep_buf(mrb_state*, const void*, size_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*);
+
+/*
+ * @param [const void*] irep code
+ * @param [size_t] size of irep buffer. If -1 is given, it is considered unrestricted.
+ */
+MRB_API mrb_value mrb_load_irep_buf_cxt(mrb_state*, const void*, size_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*);
@@ -63,10 +122,24 @@ struct mrb_insn_data {
uint8_t insn;
uint16_t a;
uint16_t b;
- uint8_t c;
+ uint16_t c;
};
-struct mrb_insn_data mrb_decode_insn(mrb_code *pc);
+struct mrb_insn_data mrb_decode_insn(const mrb_code *pc);
+
+static inline const struct mrb_irep_catch_handler *
+mrb_irep_catch_handler_table(const struct mrb_irep *irep)
+{
+ if (irep->clen > 0) {
+ return (const struct mrb_irep_catch_handler*)(irep->iseq + irep->ilen);
+ }
+ else {
+ return (const struct mrb_irep_catch_handler*)NULL;
+ }
+}
+
+#define mrb_irep_catch_handler_pack(n, v) uint32_to_bin(n, v)
+#define mrb_irep_catch_handler_unpack(v) bin_to_uint32(v)
MRB_END_DECL
diff --git a/include/mruby/istruct.h b/include/mruby/istruct.h
index 4d2393ccd..d6b6116a7 100644
--- a/include/mruby/istruct.h
+++ b/include/mruby/istruct.h
@@ -1,5 +1,5 @@
-/*
-** mruby/istruct.h - Inline structures
+/**
+** @file mruby/istruct.h - Inline structures
**
** See Copyright Notice in mruby.h
*/
@@ -19,12 +19,15 @@ MRB_BEGIN_DECL
#define ISTRUCT_DATA_SIZE (sizeof(void*) * 3)
-struct RIstruct {
+struct RIStruct {
MRB_OBJECT_HEADER;
- char inline_data[ISTRUCT_DATA_SIZE];
+ union {
+ intptr_t inline_alignment[3];
+ char inline_data[ISTRUCT_DATA_SIZE];
+ };
};
-#define RISTRUCT(obj) ((struct RIstruct*)(mrb_ptr(obj)))
+#define RISTRUCT(obj) ((struct RIStruct*)(mrb_ptr(obj)))
#define ISTRUCT_PTR(obj) (RISTRUCT(obj)->inline_data)
MRB_INLINE mrb_int mrb_istruct_size()
diff --git a/include/mruby/khash.h b/include/mruby/khash.h
index 9c40c6b80..1fb6eecbb 100644
--- a/include/mruby/khash.h
+++ b/include/mruby/khash.h
@@ -1,5 +1,5 @@
-/*
-** mruby/khash.c - Hash for mruby
+/**
+** @file mruby/khash.h - Hash for mruby
**
** See Copyright Notice in mruby.h
*/
@@ -61,7 +61,6 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
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; \
@@ -92,19 +91,28 @@ kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
khval_t: value data type
kh_is_map: (0: hash set / 1: hash map)
__hash_func: hash function
- __hash_equal: hash comparation function
+ __hash_equal: hash comparison 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) \
+ mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb); \
+ int kh_alloc_simple_##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; \
+ uint8_t *p = (uint8_t*)mrb_malloc_simple(mrb, sizeof(uint8_t)*sz/4+len*sz); \
+ if (!p) { return 1; } \
+ h->size = 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); \
+ return 0; \
+ } \
+ void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h) \
+ { \
+ if (kh_alloc_simple_##name(mrb, h)) { \
+ mrb_raise_nomemory(mrb); \
+ } \
} \
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)); \
@@ -112,7 +120,10 @@ kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
size = KHASH_MIN_SIZE; \
khash_power2(size); \
h->n_buckets = size; \
- kh_alloc_##name(mrb, h); \
+ if (kh_alloc_simple_##name(mrb, h)) { \
+ mrb_free(mrb, h); \
+ mrb_raise_nomemory(mrb); \
+ } \
return h; \
} \
kh_##name##_t *kh_init_##name(mrb_state *mrb) { \
@@ -130,7 +141,7 @@ kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
(void)mrb; \
if (h && h->ed_flags) { \
kh_fill_flags(h->ed_flags, 0xaa, h->n_buckets/4); \
- h->size = h->n_occupied = 0; \
+ h->size = 0; \
} \
} \
khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) \
@@ -174,7 +185,7 @@ kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t k, del_k, step = 0; \
- if (h->n_occupied >= khash_upper_bound(h)) { \
+ if (h->size >= khash_upper_bound(h)) { \
kh_resize_##name(mrb, h, h->n_buckets*2); \
} \
k = __hash_func(mrb,key) & khash_mask(h); \
@@ -204,7 +215,6 @@ kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
h->keys[k] = key; \
h->ed_flags[k/4] &= ~__m_empty[k%4]; \
h->size++; \
- h->n_occupied++; \
if (ret) *ret = 1; \
return k; \
} \
diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h
index da9225aaa..e09a797ed 100644
--- a/include/mruby/numeric.h
+++ b/include/mruby/numeric.h
@@ -1,5 +1,5 @@
-/*
-** mruby/numeric.h - Numeric, Integer, Float, Fixnum class
+/**
+** @file mruby/numeric.h - Numeric, Integer, Float class
**
** See Copyright Notice in mruby.h
*/
@@ -12,34 +12,36 @@
/**
* Numeric class and it's sub-classes.
*
- * Integer, Float and Fixnum
+ * Integer and Float
*/
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_POSFIXABLE(f,t) ((f) <= (t)MRB_FIXNUM_MAX)
+#define TYPED_NEGFIXABLE(f,t) ((f) >= (t)MRB_FIXNUM_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);
+#ifndef MRB_NO_FLOAT
+#ifdef MRB_INT64
+#define FIXABLE_FLOAT(f) ((f)>=-9223372036854775808.0 && (f)<9223372036854775808.0)
+#else
+#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,mrb_float)
#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);
+MRB_API mrb_value mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y);
+MRB_API mrb_value mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y);
+MRB_API mrb_value mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y);
+
+MRB_API mrb_value mrb_integer_to_str(mrb_state *mrb, mrb_value x, mrb_int base);
+MRB_API char *mrb_int_to_cstr(char *buf, size_t len, mrb_int n, mrb_int base);
+
+/* internal function(s) */
+mrb_int mrb_div_int(mrb_state *mrb, mrb_int x, mrb_int y);
+
+/* obsolete function(s); will be removed */
+#define mrb_fixnum_to_str(mrb, x, base) mrb_integer_to_str(mrb, x, base)
#ifndef __has_builtin
#define __has_builtin(x) 0
@@ -66,96 +68,113 @@ mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y);
#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);
+ return __builtin_add_overflow(augend, addend, 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);
+ return __builtin_sub_overflow(minuend, subtrahend, 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);
+ return __builtin_mul_overflow(multiplier, multiplicand, 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))
+#define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1))
static inline mrb_bool
-mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
+mrb_int_add_overflow(mrb_int a, mrb_int b, mrb_int *c)
{
- mrb_uint x = (mrb_uint)augend;
- mrb_uint y = (mrb_uint)addend;
+ mrb_uint x = (mrb_uint)a;
+ mrb_uint y = (mrb_uint)b;
mrb_uint z = (mrb_uint)(x + y);
- *sum = (mrb_int)z;
+ *c = (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_int_sub_overflow(mrb_int a, mrb_int b, mrb_int *c)
{
- mrb_uint x = (mrb_uint)minuend;
- mrb_uint y = (mrb_uint)subtrahend;
+ mrb_uint x = (mrb_uint)a;
+ mrb_uint y = (mrb_uint)b;
mrb_uint z = (mrb_uint)(x - y);
- *difference = (mrb_int)z;
+ *c = (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)
+mrb_int_mul_overflow(mrb_int a, mrb_int b, mrb_int *c)
{
-#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;
+#ifdef MRB_INT32
+ int64_t n = (int64_t)a * b;
+ *c = (mrb_int)n;
+ return n > MRB_INT_MAX || n < MRB_INT_MIN;
+#else /* MRB_INT64 */
+ if (a > 0 && b > 0 && a > MRB_INT_MAX / b) return TRUE;
+ if (a < 0 && b > 0 && a < MRB_INT_MIN / b) return TRUE;
+ if (a > 0 && b < 0 && b < MRB_INT_MIN / a) return TRUE;
+ if (a < 0 && b < 0 && (a <= MRB_INT_MIN || b <= MRB_INT_MIN || -a > MRB_INT_MAX / -b))
+ return TRUE;
+ *c = a * b;
return FALSE;
#endif
}
#undef MRB_INT_OVERFLOW_MASK
-#undef mrb_uint
-#undef MRB_UINT_MAKE
-#undef MRB_UINT_MAKE2
#endif
+#ifndef MRB_NO_FLOAT
+# include <stdint.h>
+# include <float.h>
+
+# define MRB_FLT_RADIX FLT_RADIX
+
+# ifdef MRB_USE_FLOAT32
+# define MRB_FLT_MANT_DIG FLT_MANT_DIG
+# define MRB_FLT_EPSILON FLT_EPSILON
+# define MRB_FLT_DIG FLT_DIG
+# define MRB_FLT_MIN_EXP FLT_MIN_EXP
+# define MRB_FLT_MIN FLT_MIN
+# define MRB_FLT_MIN_10_EXP FLT_MIN_10_EXP
+# define MRB_FLT_MAX_EXP FLT_MAX_EXP
+# define MRB_FLT_MAX FLT_MAX
+# define MRB_FLT_MAX_10_EXP FLT_MAX_10_EXP
+
+# else /* not MRB_USE_FLOAT32 */
+# define MRB_FLT_MANT_DIG DBL_MANT_DIG
+# define MRB_FLT_EPSILON DBL_EPSILON
+# define MRB_FLT_DIG DBL_DIG
+# define MRB_FLT_MIN_EXP DBL_MIN_EXP
+# define MRB_FLT_MIN DBL_MIN
+# define MRB_FLT_MIN_10_EXP DBL_MIN_10_EXP
+# define MRB_FLT_MAX_EXP DBL_MAX_EXP
+# define MRB_FLT_MAX DBL_MAX
+# define MRB_FLT_MAX_10_EXP DBL_MAX_10_EXP
+# endif /* MRB_USE_FLOAT32 */
+
+MRB_API mrb_value mrb_float_to_integer(mrb_state *mrb, mrb_value val);
+MRB_API mrb_float mrb_as_float(mrb_state *mrb, mrb_value x);
+
+/* internal functions */
+mrb_float mrb_div_float(mrb_float x, mrb_float y);
+mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt);
+int mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign);
+
+/* obsolete functions; will be removed */
+#define mrb_flo_to_fixnum(mrb, val) mrb_float_to_integer(mrb, val)
+#define mrb_to_flo(mrb, x) mrb_as_float(mrb, x)
+
+#endif /* MRB_NO_FLOAT */
+
MRB_END_DECL
#endif /* MRUBY_NUMERIC_H */
diff --git a/include/mruby/object.h b/include/mruby/object.h
index 25584a1d4..f75e99f1b 100644
--- a/include/mruby/object.h
+++ b/include/mruby/object.h
@@ -1,5 +1,5 @@
-/*
-** mruby/object.h - mruby object definition
+/**
+** @file mruby/object.h - mruby object definition
**
** See Copyright Notice in mruby.h
*/
@@ -8,14 +8,13 @@
#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 RClass *c; \
+ struct RBasic *gcnext; \
+ enum mrb_vtype tt:8; \
+ uint32_t color:3; \
+ uint32_t flags:21
+#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & (flag))
struct RBasic {
MRB_OBJECT_HEADER;
@@ -26,6 +25,7 @@ struct RBasic {
#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)
+#define mrb_frozen_p(o) MRB_FROZEN_P(o)
struct RObject {
MRB_OBJECT_HEADER;
@@ -33,7 +33,6 @@ struct RObject {
};
#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 {
diff --git a/include/mruby/opcode.h b/include/mruby/opcode.h
index d513ca472..249598161 100644
--- a/include/mruby/opcode.h
+++ b/include/mruby/opcode.h
@@ -1,5 +1,5 @@
-/*
-** mruby/opcode.h - RiteVM operation codes
+/**
+** @file mruby/opcode.h - RiteVM operation codes
**
** See Copyright Notice in mruby.h
*/
@@ -36,6 +36,7 @@ enum mrb_insn {
#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_BSS() do {a=READ_B(); b=READ_S(); c=READ_S();} while (0)
#define FETCH_S() do {a=READ_S();} while (0)
#define FETCH_W() do {a=READ_W();} while (0)
@@ -45,6 +46,7 @@ enum mrb_insn {
#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_BSS_1() do {a=READ_S(); b=READ_S();c=READ_S();} while (0)
#define FETCH_S_1() FETCH_S()
#define FETCH_W_1() FETCH_W()
@@ -54,6 +56,7 @@ enum mrb_insn {
#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_BSS_2() FETCH_BSS()
#define FETCH_S_2() FETCH_S()
#define FETCH_W_2() FETCH_W()
@@ -63,6 +66,7 @@ enum mrb_insn {
#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_BSS_3() FETCH_BSS_1()
#define FETCH_S_3() FETCH_S()
#define FETCH_W_3() FETCH_W()
diff --git a/include/mruby/ops.h b/include/mruby/ops.h
index f23bb1b0b..fae6872cc 100644
--- a/include/mruby/ops.h
+++ b/include/mruby/ops.h
@@ -1,11 +1,12 @@
/* 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)
+ + Z: no operand
+ + B: 8bit
+ + BB: 8+8bit
+ + BBB: 8+8+8bit
+ + BS: 8+16bit
+ + BSS: 8+16+16bit
+ + S: 16bit
+ + W: 24bit
*/
/*-----------------------------------------------------------------------
@@ -25,6 +26,8 @@ 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(LOADI16, BS) /* R(a) = mrb_int(b) */
+OPCODE(LOADI32, BSS) /* R(a) = mrb_int((b<<16)+c) */
OPCODE(LOADSYM, BB) /* R(a) = Syms(b) */
OPCODE(LOADNIL, B) /* R(a) = nil */
OPCODE(LOADSELF, B) /* R(a) = self */
@@ -44,43 +47,41 @@ 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(JMP, S) /* pc+=a */
+OPCODE(JMPIF, BS) /* if R(a) pc+=b */
+OPCODE(JMPNOT, BS) /* if !R(a) pc+=b */
+OPCODE(JMPNIL, BS) /* if R(a)==nil pc+=b */
+OPCODE(JMPUW, S) /* unwind_and_jump_to(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(RAISEIF, B) /* raise(R(a)) if R(a) */
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(SENDB, BBB) /* R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c),&R(a+c+1)) */
+OPCODE(SENDVK, BB) /* R(a) = call(R(a),Syms(b),*R(a+1),**(a+2),&R(a+3)) # todo */
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(KEY_P, BB) /* R(a) = kdict.key?(Syms(b)) */
+OPCODE(KEYEND, Z) /* raise unless kdict.empty? */
+OPCODE(KARG, BB) /* R(a) = kdict[Syms(b)]; kdict.delete(Syms(b)) */
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, BB) /* R(a) = R(a)+R(a+1) (Syms[b]=:+) */
-OPCODE(ADDI, BBB) /* R(a) = R(a)+mrb_int(c) (Syms[b]=:+) */
-OPCODE(SUB, BB) /* R(a) = R(a)-R(a+1) (Syms[b]=:-) */
-OPCODE(SUBI, BBB) /* R(a) = R(a)-C (Syms[b]=:-) */
-OPCODE(MUL, BB) /* R(a) = R(a)*R(a+1) (Syms[b]=:*) */
-OPCODE(DIV, BB) /* R(a) = R(a)/R(a+1) (Syms[b]=:/) */
-OPCODE(EQ, BB) /* R(a) = R(a)==R(a+1) (Syms[b]=:==) */
-OPCODE(LT, BB) /* R(a) = R(a)<R(a+1) (Syms[b]=:<) */
-OPCODE(LE, BB) /* R(a) = R(a)<=R(a+1) (Syms[b]=:<=) */
-OPCODE(GT, BB) /* R(a) = R(a)>R(a+1) (Syms[b]=:>) */
-OPCODE(GE, BB) /* R(a) = R(a)>=R(a+1) (Syms[b]=:>=) */
+OPCODE(ADD, B) /* R(a) = R(a)+R(a+1) */
+OPCODE(ADDI, BB) /* R(a) = R(a)+mrb_int(b) */
+OPCODE(SUB, B) /* R(a) = R(a)-R(a+1) */
+OPCODE(SUBI, BB) /* R(a) = R(a)-mrb_int(b) */
+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(LE, B) /* R(a) = R(a)<=R(a+1) */
+OPCODE(GT, 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)) */
@@ -88,30 +89,30 @@ 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(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(STRING, BB) /* R(a) = str_dup(Pool(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(HASH, BB) /* R(a) = hash_new(R(a),R(a+1)..R(a+b*2-1)) */
+OPCODE(HASHADD, BB) /* R(a) = hash_push(R(a),R(a+1)..R(a+b*2)) */
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(LAMBDA, BB) /* R(a) = lambda(Irep(b),L_LAMBDA) */
+OPCODE(BLOCK, BB) /* R(a) = lambda(Irep(b),L_BLOCK) */
+OPCODE(METHOD, BB) /* R(a) = lambda(Irep(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(EXEC, BB) /* R(a) = blockexec(R(a),Irep(b)) */
+OPCODE(DEF, BB) /* R(a).newmethod(Syms(b),R(a+1)); R(a) = Syms(b) */
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(ERR, B) /* raise(LocalJumpError, Pool(a)) */
+OPCODE(EXT1, Z) /* make 1st operand (a) 16bit */
+OPCODE(EXT2, Z) /* make 2nd operand (b) 16bit */
OPCODE(EXT3, Z) /* make 1st and 2nd operands 16bit */
OPCODE(STOP, Z) /* stop VM */
diff --git a/include/mruby/presym.h b/include/mruby/presym.h
new file mode 100644
index 000000000..066b675e7
--- /dev/null
+++ b/include/mruby/presym.h
@@ -0,0 +1,40 @@
+/**
+** @file mruby/presym.h - Preallocated Symbols
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_PRESYM_H
+#define MRUBY_PRESYM_H
+
+#if defined(MRB_NO_PRESYM)
+# include <mruby/presym/disable.h>
+#elif !defined(MRB_PRESYM_SCANNING)
+# include <mruby/presym/enable.h>
+#endif
+
+/*
+ * Where `mrb_intern_lit` is allowed for symbol interning, it is directly
+ * replaced by the symbol ID if presym is enabled by using the following
+ * macros.
+ *
+ * MRB_OPSYM(xor) //=> ^ (Operator)
+ * MRB_CVSYM(xor) //=> @@xor (Class Variable)
+ * MRB_IVSYM(xor) //=> @xor (Instance Variable)
+ * MRB_SYM_B(xor) //=> xor! (Method with Bang)
+ * MRB_SYM_Q(xor) //=> xor? (Method with Question mark)
+ * MRB_SYM_E(xor) //=> xor= (Method with Equal)
+ * MRB_SYM(xor) //=> xor (Word characters)
+ *
+ * For `MRB_OPSYM`, specify the names corresponding to operators (see
+ * `MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that
+ * can be specified for it). Other than that, describe only word characters
+ * excluding leading and ending punctuations.
+ *
+ * These macros are expanded to `mrb_intern_lit` if presym is disabled,
+ * therefore the mruby state variable is required. The above macros can be
+ * used when the variable name is `mrb`. If you want to use other variable
+ * names, you need to use macros with `_2` suffix, such as `MRB_SYM_2`.
+ */
+
+#endif /* MRUBY_PRESYM_H */
diff --git a/include/mruby/presym/disable.h b/include/mruby/presym/disable.h
new file mode 100644
index 000000000..45843fbf8
--- /dev/null
+++ b/include/mruby/presym/disable.h
@@ -0,0 +1,70 @@
+/**
+** @file mruby/presym/disable.h - Disable Preallocated Symbols
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_PRESYM_DISABLE_H
+#define MRUBY_PRESYM_DISABLE_H
+
+#include <string.h>
+
+#define MRB_PRESYM_MAX 0
+
+#define MRB_OPSYM(name) MRB_OPSYM__##name(mrb)
+#define MRB_CVSYM(name) mrb_intern_lit(mrb, "@@" #name)
+#define MRB_IVSYM(name) mrb_intern_lit(mrb, "@" #name)
+#define MRB_SYM_B(name) mrb_intern_lit(mrb, #name "!")
+#define MRB_SYM_Q(name) mrb_intern_lit(mrb, #name "?")
+#define MRB_SYM_E(name) mrb_intern_lit(mrb, #name "=")
+#define MRB_SYM(name) mrb_intern_lit(mrb, #name)
+
+#define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name(mrb)
+#define MRB_CVSYM_2(mrb, name) mrb_intern_lit(mrb, "@@" #name)
+#define MRB_IVSYM_2(mrb, name) mrb_intern_lit(mrb, "@" #name)
+#define MRB_SYM_B_2(mrb, name) mrb_intern_lit(mrb, #name "!")
+#define MRB_SYM_Q_2(mrb, name) mrb_intern_lit(mrb, #name "?")
+#define MRB_SYM_E_2(mrb, name) mrb_intern_lit(mrb, #name "=")
+#define MRB_SYM_2(mrb, name) mrb_intern_lit(mrb, #name)
+
+#define MRB_OPSYM__not(mrb) mrb_intern_lit(mrb, "!")
+#define MRB_OPSYM__mod(mrb) mrb_intern_lit(mrb, "%")
+#define MRB_OPSYM__and(mrb) mrb_intern_lit(mrb, "&")
+#define MRB_OPSYM__mul(mrb) mrb_intern_lit(mrb, "*")
+#define MRB_OPSYM__add(mrb) mrb_intern_lit(mrb, "+")
+#define MRB_OPSYM__sub(mrb) mrb_intern_lit(mrb, "-")
+#define MRB_OPSYM__div(mrb) mrb_intern_lit(mrb, "/")
+#define MRB_OPSYM__lt(mrb) mrb_intern_lit(mrb, "<")
+#define MRB_OPSYM__gt(mrb) mrb_intern_lit(mrb, ">")
+#define MRB_OPSYM__xor(mrb) mrb_intern_lit(mrb, "^")
+#define MRB_OPSYM__tick(mrb) mrb_intern_lit(mrb, "`")
+#define MRB_OPSYM__or(mrb) mrb_intern_lit(mrb, "|")
+#define MRB_OPSYM__neg(mrb) mrb_intern_lit(mrb, "~")
+#define MRB_OPSYM__neq(mrb) mrb_intern_lit(mrb, "!=")
+#define MRB_OPSYM__nmatch(mrb) mrb_intern_lit(mrb, "!~")
+#define MRB_OPSYM__andand(mrb) mrb_intern_lit(mrb, "&&")
+#define MRB_OPSYM__pow(mrb) mrb_intern_lit(mrb, "**")
+#define MRB_OPSYM__plus(mrb) mrb_intern_lit(mrb, "+@")
+#define MRB_OPSYM__minus(mrb) mrb_intern_lit(mrb, "-@")
+#define MRB_OPSYM__lshift(mrb) mrb_intern_lit(mrb, "<<")
+#define MRB_OPSYM__le(mrb) mrb_intern_lit(mrb, "<=")
+#define MRB_OPSYM__eq(mrb) mrb_intern_lit(mrb, "==")
+#define MRB_OPSYM__match(mrb) mrb_intern_lit(mrb, "=~")
+#define MRB_OPSYM__ge(mrb) mrb_intern_lit(mrb, ">=")
+#define MRB_OPSYM__rshift(mrb) mrb_intern_lit(mrb, ">>")
+#define MRB_OPSYM__aref(mrb) mrb_intern_lit(mrb, "[]")
+#define MRB_OPSYM__oror(mrb) mrb_intern_lit(mrb, "||")
+#define MRB_OPSYM__cmp(mrb) mrb_intern_lit(mrb, "<=>")
+#define MRB_OPSYM__eqq(mrb) mrb_intern_lit(mrb, "===")
+#define MRB_OPSYM__aset(mrb) mrb_intern_lit(mrb, "[]=")
+
+#define MRB_PRESYM_DEFINE_VAR_AND_INITER(name, size, ...) \
+ static mrb_sym name[size]; \
+ static void presym_init_##name(mrb_state *mrb) { \
+ mrb_sym name__[] = {__VA_ARGS__}; \
+ memcpy(name, name__, sizeof(name)); \
+ }
+
+#define MRB_PRESYM_INIT_SYMBOLS(mrb, name) presym_init_##name(mrb)
+
+#endif /* MRUBY_PRESYM_DISABLE_H */
diff --git a/include/mruby/presym/enable.h b/include/mruby/presym/enable.h
new file mode 100644
index 000000000..8ca0c3cc6
--- /dev/null
+++ b/include/mruby/presym/enable.h
@@ -0,0 +1,37 @@
+/**
+** @file mruby/presym/enable.h - Enable Preallocated Symbols
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_PRESYM_ENABLE_H
+#define MRUBY_PRESYM_ENABLE_H
+
+#include <mruby/presym/id.h>
+
+#define MRB_OPSYM(name) MRB_OPSYM__##name
+#define MRB_CVSYM(name) MRB_CVSYM__##name
+#define MRB_IVSYM(name) MRB_IVSYM__##name
+#define MRB_SYM_B(name) MRB_SYM_B__##name
+#define MRB_SYM_Q(name) MRB_SYM_Q__##name
+#define MRB_SYM_E(name) MRB_SYM_E__##name
+#define MRB_SYM(name) MRB_SYM__##name
+
+#define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name
+#define MRB_CVSYM_2(mrb, name) MRB_CVSYM__##name
+#define MRB_IVSYM_2(mrb, name) MRB_IVSYM__##name
+#define MRB_SYM_B_2(mrb, name) MRB_SYM_B__##name
+#define MRB_SYM_Q_2(mrb, name) MRB_SYM_Q__##name
+#define MRB_SYM_E_2(mrb, name) MRB_SYM_E__##name
+#define MRB_SYM_2(mrb, name) MRB_SYM__##name
+
+#define MRB_PRESYM_DEFINE_VAR_AND_INITER(name, size, ...) \
+ static const mrb_sym name[] = {__VA_ARGS__};
+
+#define MRB_PRESYM_INIT_SYMBOLS(mrb, name) (void)(mrb)
+
+/* use MRB_SYM() for E_RUNTIME_ERROR etc. */
+#undef MRB_ERROR_SYM
+#define MRB_ERROR_SYM(sym) MRB_SYM(sym)
+
+#endif /* MRUBY_PRESYM_ENABLE_H */
diff --git a/include/mruby/presym/scanning.h b/include/mruby/presym/scanning.h
new file mode 100644
index 000000000..fab0743f7
--- /dev/null
+++ b/include/mruby/presym/scanning.h
@@ -0,0 +1,73 @@
+/**
+** @file mruby/presym/scanning.h - Scanning Preallocated Symbols
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_PRESYM_SCANNING_H
+#define MRUBY_PRESYM_SCANNING_H
+
+#define MRB_PRESYM_SCANNING_TAGGED(arg) <@! arg !@>
+
+#undef mrb_intern_lit
+#define mrb_intern_lit(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_intern_cstr(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_method(mrb, c, name, f, a) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_class_method(mrb, c, name, f, a) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_singleton_method(mrb, c, name, f, a) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_class(mrb, name, s) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_class_under(mrb, o, name, s) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_module(mrb, name) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_module_under(mrb, o, name) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_module_function(mrb, c, name, f, s) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_const(mrb, c, name, v) MRB_PRESYM_SCANNING_TAGGED(name)
+#define mrb_define_global_const(mrb, name, v) MRB_PRESYM_SCANNING_TAGGED(name)
+
+#define MRB_OPSYM(name) MRB_OPSYM__##name(mrb)
+#define MRB_CVSYM(name) MRB_PRESYM_SCANNING_TAGGED("@@" #name)
+#define MRB_IVSYM(name) MRB_PRESYM_SCANNING_TAGGED("@" #name)
+#define MRB_SYM_B(name) MRB_PRESYM_SCANNING_TAGGED(#name "!")
+#define MRB_SYM_Q(name) MRB_PRESYM_SCANNING_TAGGED(#name "?")
+#define MRB_SYM_E(name) MRB_PRESYM_SCANNING_TAGGED(#name "=")
+#define MRB_SYM(name) MRB_PRESYM_SCANNING_TAGGED(#name)
+
+#define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name(mrb)
+#define MRB_CVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("@@" #name)
+#define MRB_IVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("@" #name)
+#define MRB_SYM_B_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "!")
+#define MRB_SYM_Q_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "?")
+#define MRB_SYM_E_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "=")
+#define MRB_SYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name)
+
+#define MRB_OPSYM__not(mrb) MRB_PRESYM_SCANNING_TAGGED("!")
+#define MRB_OPSYM__mod(mrb) MRB_PRESYM_SCANNING_TAGGED("%")
+#define MRB_OPSYM__and(mrb) MRB_PRESYM_SCANNING_TAGGED("&")
+#define MRB_OPSYM__mul(mrb) MRB_PRESYM_SCANNING_TAGGED("*")
+#define MRB_OPSYM__add(mrb) MRB_PRESYM_SCANNING_TAGGED("+")
+#define MRB_OPSYM__sub(mrb) MRB_PRESYM_SCANNING_TAGGED("-")
+#define MRB_OPSYM__div(mrb) MRB_PRESYM_SCANNING_TAGGED("/")
+#define MRB_OPSYM__lt(mrb) MRB_PRESYM_SCANNING_TAGGED("<")
+#define MRB_OPSYM__gt(mrb) MRB_PRESYM_SCANNING_TAGGED(">")
+#define MRB_OPSYM__xor(mrb) MRB_PRESYM_SCANNING_TAGGED("^")
+#define MRB_OPSYM__tick(mrb) MRB_PRESYM_SCANNING_TAGGED("`")
+#define MRB_OPSYM__or(mrb) MRB_PRESYM_SCANNING_TAGGED("|")
+#define MRB_OPSYM__neg(mrb) MRB_PRESYM_SCANNING_TAGGED("~")
+#define MRB_OPSYM__neq(mrb) MRB_PRESYM_SCANNING_TAGGED("!=")
+#define MRB_OPSYM__nmatch(mrb) MRB_PRESYM_SCANNING_TAGGED("!~")
+#define MRB_OPSYM__andand(mrb) MRB_PRESYM_SCANNING_TAGGED("&&")
+#define MRB_OPSYM__pow(mrb) MRB_PRESYM_SCANNING_TAGGED("**")
+#define MRB_OPSYM__plus(mrb) MRB_PRESYM_SCANNING_TAGGED("+@")
+#define MRB_OPSYM__minus(mrb) MRB_PRESYM_SCANNING_TAGGED("-@")
+#define MRB_OPSYM__lshift(mrb) MRB_PRESYM_SCANNING_TAGGED("<<")
+#define MRB_OPSYM__le(mrb) MRB_PRESYM_SCANNING_TAGGED("<=")
+#define MRB_OPSYM__eq(mrb) MRB_PRESYM_SCANNING_TAGGED("==")
+#define MRB_OPSYM__match(mrb) MRB_PRESYM_SCANNING_TAGGED("=~")
+#define MRB_OPSYM__ge(mrb) MRB_PRESYM_SCANNING_TAGGED(">=")
+#define MRB_OPSYM__rshift(mrb) MRB_PRESYM_SCANNING_TAGGED(">>")
+#define MRB_OPSYM__aref(mrb) MRB_PRESYM_SCANNING_TAGGED("[]")
+#define MRB_OPSYM__oror(mrb) MRB_PRESYM_SCANNING_TAGGED("||")
+#define MRB_OPSYM__cmp(mrb) MRB_PRESYM_SCANNING_TAGGED("<=>")
+#define MRB_OPSYM__eqq(mrb) MRB_PRESYM_SCANNING_TAGGED("===")
+#define MRB_OPSYM__aset(mrb) MRB_PRESYM_SCANNING_TAGGED("[]=")
+
+#endif /* MRUBY_PRESYM_SCANNING_H */
diff --git a/include/mruby/proc.h b/include/mruby/proc.h
index f428e6666..31f40d379 100644
--- a/include/mruby/proc.h
+++ b/include/mruby/proc.h
@@ -1,5 +1,5 @@
-/*
-** mruby/proc.h - Proc class
+/**
+** @file mruby/proc.h - Proc class
**
** See Copyright Notice in mruby.h
*/
@@ -22,24 +22,29 @@ struct REnv {
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)
+/* flags (21bits): 1(close):1(touched):1(heap):8(cioff/bidx):8(stack_len) */
+#define MRB_ENV_SET_LEN(e,len) ((e)->flags = (((e)->flags & ~0xff)|((unsigned int)(len) & 0xff)))
+#define MRB_ENV_LEN(e) ((mrb_int)((e)->flags & 0xff))
+#define MRB_ENV_CLOSED (1<<20)
+#define MRB_ENV_TOUCHED (1<<19)
+#define MRB_ENV_HEAPED (1<<18)
+#define MRB_ENV_CLOSE(e) ((e)->flags |= MRB_ENV_CLOSED)
+#define MRB_ENV_TOUCH(e) ((e)->flags |= MRB_ENV_TOUCHED)
+#define MRB_ENV_HEAP(e) ((e)->flags |= MRB_ENV_HEAPED)
+#define MRB_ENV_HEAP_P(e) ((e)->flags & MRB_ENV_HEAPED)
+#define MRB_ENV_ONSTACK_P(e) (((e)->flags & MRB_ENV_CLOSED) == 0)
+#define MRB_ENV_BIDX(e) (((e)->flags >> 8) & 0xff)
+#define MRB_ENV_SET_BIDX(e,idx) ((e)->flags = (((e)->flags & ~(0xff<<8))|((unsigned int)(idx) & 0xff)<<8))
void mrb_env_unshare(mrb_state*, struct REnv*);
struct RProc {
MRB_OBJECT_HEADER;
union {
- mrb_irep *irep;
+ const mrb_irep *irep;
mrb_func_t func;
} body;
- struct RProc *upper;
+ const struct RProc *upper;
union {
struct RClass *target_class;
struct REnv *env;
@@ -52,7 +57,7 @@ struct RProc {
#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_KDICT(a) (((a) >> 1) & 0x1)
#define MRB_ASPEC_BLOCK(a) ((a) & 1)
#define MRB_PROC_CFUNC_FL 128
@@ -69,11 +74,11 @@ struct RProc {
#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);\
+ 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);\
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)(tc));\
}\
} while (0)
#define MRB_PROC_SCOPE 2048
@@ -81,50 +86,123 @@ struct RProc {
#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*);
+struct RProc *mrb_proc_new(mrb_state*, const mrb_irep*);
+struct RProc *mrb_closure_new(mrb_state*, const 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);
+mrb_int mrb_proc_arity(const struct RProc *p);
/* 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);
+MRB_API struct RProc *mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv);
+MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx);
/* 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 1
+#define MRB_METHOD_NOARG_FL 2
+
+#ifndef MRB_USE_METHOD_T_STRUCT
-#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_NOARG_P(m) ((((uintptr_t)(m))&MRB_METHOD_NOARG_FL)?1:0)
+#define MRB_METHOD_NOARG_SET(m) ((m)=(mrb_method_t)(((uintptr_t)(m))|MRB_METHOD_NOARG_FL))
+#define MRB_METHOD_FUNC(m) ((mrb_func_t)((uintptr_t)(m)>>2))
+#define MRB_METHOD_FROM_FUNC(m,fn) ((m)=(mrb_method_t)((((uintptr_t)(fn))<<2)|MRB_METHOD_FUNC_FL))
+#define MRB_METHOD_FROM_PROC(m,pr) ((m)=(mrb_method_t)(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_P(m) ((m).flags&MRB_METHOD_FUNC_FL)
+#define MRB_METHOD_NOARG_P(m) (((m).flags&MRB_METHOD_NOARG_FL)?1:0)
#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_NOARG_SET(m) do{(m).flags|=MRB_METHOD_NOARG_FL;}while(0)
+#define MRB_METHOD_FROM_FUNC(m,fn) do{(m).flags=MRB_METHOD_FUNC_FL;(m).func=(fn);}while(0)
+#define MRB_METHOD_FROM_PROC(m,pr) do{(m).flags=0;(m).proc=(struct RProc*)(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 */
+#endif /* MRB_USE_METHOD_T_STRUCT */
#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 <mruby/khash.h>
-KHASH_DECLARE(mt, mrb_sym, mrb_method_t, TRUE)
+
+MRB_API mrb_value mrb_load_proc(mrb_state *mrb, const struct RProc *proc);
+
+static inline void
+mrb_vm_ci_proc_set(mrb_callinfo *ci, const struct RProc *p)
+{
+ ci->proc = p;
+ ci->pc = (p && !MRB_PROC_CFUNC_P(p)) ? p->body.irep->iseq : NULL;
+}
+
+static inline struct RClass *
+mrb_vm_ci_target_class(const mrb_callinfo *ci)
+{
+ if (ci->u.env && ci->u.env->tt == MRB_TT_ENV) {
+ return ci->u.env->c;
+ }
+ else {
+ return ci->u.target_class;
+ }
+}
+
+static inline void
+mrb_vm_ci_target_class_set(mrb_callinfo *ci, struct RClass *tc)
+{
+ struct REnv *e = ci->u.env;
+ if (e) {
+ if (e->tt == MRB_TT_ENV) {
+ e->c = tc;
+ }
+ else {
+ ci->u.target_class = tc;
+ }
+ }
+}
+
+static inline struct REnv *
+mrb_vm_ci_env(const mrb_callinfo *ci)
+{
+ if (ci->u.env && ci->u.env->tt == MRB_TT_ENV) {
+ return ci->u.env;
+ }
+ else {
+ return NULL;
+ }
+}
+
+static inline void
+mrb_vm_ci_env_set(mrb_callinfo *ci, struct REnv *e)
+{
+ if (ci->u.env) {
+ if (ci->u.env->tt == MRB_TT_ENV) {
+ if (e) {
+ e->c = ci->u.env->c;
+ ci->u.env = e;
+ }
+ else {
+ ci->u.target_class = ci->u.env->c;
+ }
+ }
+ else {
+ if (e) {
+ e->c = ci->u.target_class;
+ ci->u.env = e;
+ }
+ }
+ }
+ else {
+ ci->u.env = e;
+ }
+}
MRB_END_DECL
diff --git a/include/mruby/range.h b/include/mruby/range.h
index b166e586b..fea700c24 100644
--- a/include/mruby/range.h
+++ b/include/mruby/range.h
@@ -1,5 +1,5 @@
-/*
-** mruby/range.h - Range class
+/**
+** @file mruby/range.h - Range class
**
** See Copyright Notice in mruby.h
*/
@@ -14,20 +14,43 @@
*/
MRB_BEGIN_DECL
+#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT) || defined(MRB_WORD_BOXING)
+# define MRB_RANGE_EMBED
+#endif
+
+#ifdef MRB_RANGE_EMBED
+struct RRange {
+ MRB_OBJECT_HEADER;
+ mrb_value beg;
+ mrb_value end;
+ mrb_bool excl;
+};
+# define mrb_gc_free_range(mrb, p) ((void)0)
+# define RANGE_BEG(p) ((p)->beg)
+# define RANGE_END(p) ((p)->end)
+#else
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_bool excl;
};
+# define mrb_gc_free_range(mrb, p) mrb_free(mrb, (p)->edges)
+# define RANGE_BEG(p) ((p)->edges->beg)
+# define RANGE_END(p) ((p)->edges->end)
+#endif
-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))
+#define mrb_range_beg(mrb, r) RANGE_BEG(mrb_range_ptr(mrb, r))
+#define mrb_range_end(mrb, r) RANGE_END(mrb_range_ptr(mrb, r))
+#define mrb_range_excl_p(mrb, r) RANGE_EXCL(mrb_range_ptr(mrb, r))
+#define mrb_range_raw_ptr(r) ((struct RRange*)mrb_ptr(r))
+#define mrb_range_value(p) mrb_obj_value((void*)(p))
+#define RANGE_EXCL(p) ((p)->excl)
+
+MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value range);
/*
* Initializes a Range.
@@ -41,8 +64,15 @@ MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v);
*/
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);
+enum mrb_range_beg_len {
+ MRB_RANGE_TYPE_MISMATCH = 0, /* (failure) not range */
+ MRB_RANGE_OK = 1, /* (success) range */
+ MRB_RANGE_OUT = 2 /* (failure) out of range */
+};
+
+MRB_API enum mrb_range_beg_len 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));
+void mrb_gc_mark_range(mrb_state *mrb, struct RRange *r);
MRB_END_DECL
diff --git a/include/mruby/re.h b/include/mruby/re.h
index 1d09d06c9..2d48019cf 100644
--- a/include/mruby/re.h
+++ b/include/mruby/re.h
@@ -1,5 +1,5 @@
-/*
-** mruby/re.h - Regexp class
+/**
+** @file mruby/re.h - Regexp class
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/string.h b/include/mruby/string.h
index 481b2fb38..9c88a0856 100644
--- a/include/mruby/string.h
+++ b/include/mruby/string.h
@@ -1,5 +1,5 @@
-/*
-** mruby/string.h - String class
+/**
+** @file mruby/string.h - String class
**
** See Copyright Notice in mruby.h
*/
@@ -16,43 +16,53 @@ MRB_BEGIN_DECL
extern const char mrb_digitmap[];
-#define RSTRING_EMBED_LEN_MAX ((mrb_int)(sizeof(void*) * 3 - 1))
+#define RSTRING_EMBED_LEN_MAX \
+ ((mrb_int)(sizeof(void*) * 3 + sizeof(void*) - 32 / CHAR_BIT - 1))
struct RString {
MRB_OBJECT_HEADER;
union {
struct {
- mrb_int len;
+ mrb_ssize len;
union {
- mrb_int capa;
+ mrb_ssize capa;
struct mrb_shared_string *shared;
struct RString *fshared;
} aux;
char *ptr;
} heap;
- char ary[RSTRING_EMBED_LEN_MAX + 1];
} as;
};
+struct RStringEmbed {
+ MRB_OBJECT_HEADER;
+ char ary[RSTRING_EMBED_LEN_MAX+1];
+};
+
+#define RSTR_SET_TYPE_FLAG(s, type) (RSTR_UNSET_TYPE_FLAG(s), (s)->flags |= MRB_STR_##type)
+#define RSTR_UNSET_TYPE_FLAG(s) ((s)->flags &= ~(MRB_STR_TYPE_MASK|MRB_STR_EMBED_LEN_MASK))
#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;\
+ (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);\
+ (s)->as.heap.len = (mrb_ssize)(n);\
}\
} while (0)
+#define RSTR_EMBED_PTR(s) (((struct RStringEmbed*)(s))->ary)
#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_EMBEDDABLE_P(len) ((len) <= RSTRING_EMBED_LEN_MAX)
+
+#define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_PTR(s) : (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)
@@ -68,10 +78,21 @@ struct RString {
#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
-#define RSTR_POOL_P(s) ((s)->flags & MRB_STR_POOL)
-#define RSTR_SET_POOL_FLAG(s) ((s)->flags |= MRB_STR_POOL)
+#ifdef MRB_UTF8_STRING
+# define RSTR_ASCII_P(s) ((s)->flags & MRB_STR_ASCII)
+# define RSTR_SET_ASCII_FLAG(s) ((s)->flags |= MRB_STR_ASCII)
+# define RSTR_UNSET_ASCII_FLAG(s) ((s)->flags &= ~MRB_STR_ASCII)
+# define RSTR_WRITE_ASCII_FLAG(s, v) (RSTR_UNSET_ASCII_FLAG(s), (s)->flags |= v)
+# define RSTR_COPY_ASCII_FLAG(dst, src) RSTR_WRITE_ASCII_FLAG(dst, RSTR_ASCII_P(src))
+#else
+# define RSTR_ASCII_P(s) (void)0
+# define RSTR_SET_ASCII_FLAG(s) (void)0
+# define RSTR_UNSET_ASCII_FLAG(s) (void)0
+# define RSTR_WRITE_ASCII_FLAG(s, v) (void)0
+# define RSTR_COPY_ASCII_FLAG(dst, src) (void)0
+#endif
-/*
+/**
* Returns a pointer from a Ruby string
*/
#define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s)))
@@ -82,32 +103,36 @@ struct RString {
#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 RSTRING_CSTR(mrb,s) mrb_string_cstr(mrb, s)
#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 8 /* type flags up to here */
+#define MRB_STR_ASCII 16
#define MRB_STR_EMBED_LEN_SHIFT 6
+#define MRB_STR_EMBED_LEN_BIT 5
+#define MRB_STR_EMBED_LEN_MASK (((1 << MRB_STR_EMBED_LEN_BIT) - 1) << MRB_STR_EMBED_LEN_SHIFT)
+#define MRB_STR_TYPE_MASK 15
void mrb_gc_free_str(mrb_state*, struct RString*);
-MRB_API void mrb_str_modify(mrb_state*, struct RString*);
-/*
+MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s);
+/* mrb_str_modify() with keeping ASCII flag if set */
+MRB_API void mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s);
+
+/**
* 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);
+MRB_API mrb_int mrb_str_index(mrb_state *mrb, mrb_value str, const char *p, mrb_int len, mrb_int offset);
#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:
+ * Example:
*
- * !!!c
* int
* main(int argc,
* char **argv)
@@ -129,32 +154,30 @@ MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_i
* // Concatenates str2 to str1.
* mrb_str_concat(mrb, str1, str2);
*
- * // Prints new Concatenated Ruby string.
- * mrb_p(mrb, str1);
- *
- * mrb_close(mrb);
- * return 0;
- * }
+ * // Prints new Concatenated Ruby string.
+ * mrb_p(mrb, str1);
*
+ * mrb_close(mrb);
+ * return 0;
+ * }
*
- * Result:
+ * 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.
+ * @param mrb The current mruby state.
+ * @param self String to concatenate.
+ * @param 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);
+MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other);
-/*
+/**
* Adds two strings together.
*
*
- * Example:
+ * Example:
*
- * !!!c
* int
* main(int argc,
* char **argv)
@@ -181,52 +204,51 @@ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
* // Concatenates both Ruby strings.
* c = mrb_str_plus(mrb, a, b);
*
- * // Prints new Concatenated Ruby string.
- * mrb_p(mrb, c);
+ * // Prints new Concatenated Ruby string.
+ * mrb_p(mrb, c);
*
- * mrb_close(mrb);
- * return 0;
- * }
+ * mrb_close(mrb);
+ * return 0;
+ * }
*
*
- * Result:
+ * 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.
+ * @param mrb The current mruby state.
+ * @param a First string to concatenate.
+ * @param 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);
+MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b);
-/*
+/**
* Converts pointer into a Ruby string.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [void*] p The pointer to convert to Ruby string.
+ * @param mrb The current mruby state.
+ * @param 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*);
+MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p);
-/*
+/**
* 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.
+ * @param mrb The current mruby state.
+ * @param 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)
@@ -251,21 +273,20 @@ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
*
* Result:
*
- * => "Hello"
+ * => "Hello"
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str The Ruby string to resize.
- * @param [mrb_value] len The length.
+ * @param mrb The current mruby state.
+ * @param str The Ruby string to resize.
+ * @param 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:
+ * Example:
*
- * !!!c
* int
* main(int argc,
* char const **argv)
@@ -291,59 +312,59 @@ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
* return 0;
* }
*
- * Result:
+ * 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.
+ * @param mrb The current mruby state.
+ * @param str Ruby string.
+ * @param beg The beginning point of the sub-string.
+ * @param 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.
+ * @param mrb The current mruby state.
+ * @param 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_check_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);
+#define mrb_str_buf_new(mrb, capa) mrb_str_new_capa(mrb, (capa))
-MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr);
+/* NULL terminated C string from mrb_value */
+MRB_API const char *mrb_string_cstr(mrb_state *mrb, mrb_value str);
+/* NULL terminated C string from mrb_value; `str` will be updated */
+MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *str);
+/* obsolete: use RSTRING_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.
- */
+/* obsolete: use RSTRING_LEN() */
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.
+ * @param mrb The current mruby state.
+ * @param 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.
+ * @param mrb The current mruby state.
+ * @param self Ruby string.
* @return [mrb_value] A symbol.
*/
MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self);
@@ -351,39 +372,41 @@ 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.
+ * obsolete: use `mrb_obj_as_string()` instead.
*/
-MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
+#define mrb_str_to_str(mrb, str) mrb_obj_as_string(mrb, 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.
+ * @param mrb The current mruby state.
+ * @param str1 Ruby string to compare.
+ * @param 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.
+/**
+ * Returns a concatenated 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.
+ * @param mrb The current mruby state.
+ * @param str Ruby string.
+ * @param ptr A C string.
+ * @param 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.
+/**
+ * Returns a concatenated 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 mrb The current mruby state.
+ * @param str Ruby string.
+ * @param ptr A C string.
* @return [mrb_value] A Ruby string.
* @see mrb_str_cat
*/
@@ -391,50 +414,54 @@ MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *pt
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.
+ * - Returned 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.
+ * @param mrb The current mruby state.
+ * @param 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_bool mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp);
+mrb_value mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
+
+#ifdef MRB_UTF8_STRING
+mrb_int mrb_utf8len(const char *str, const char *end);
+mrb_int mrb_utf8_strlen(const char *str, mrb_int byte_len);
+#endif
+
MRB_END_DECL
#endif /* MRUBY_STRING_H */
diff --git a/include/mruby/throw.h b/include/mruby/throw.h
index 5d3d214e7..52171e9b0 100644
--- a/include/mruby/throw.h
+++ b/include/mruby/throw.h
@@ -1,5 +1,5 @@
-/*
-** mruby/throw.h - mruby exception throwing handler
+/**
+** @file mruby/throw.h - mruby exception throwing handler
**
** See Copyright Notice in mruby.h
*/
@@ -7,21 +7,27 @@
#ifndef MRB_THROW_H
#define MRB_THROW_H
-#if defined(MRB_ENABLE_CXX_ABI)
+#if defined(MRB_USE_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)
+#if defined(MRB_USE_CXX_EXCEPTION)
-#define MRB_TRY(buf) do { try {
+# if 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) } } while(0)
+#define MRB_END_EXC(buf) }
#define MRB_THROW(buf) throw((buf)->impl)
typedef mrb_int mrb_jmpbuf_impl;
+# else
+# error "need to be compiled with C++ compiler"
+# endif /* __cplusplus */
+
#else
#include <setjmp.h>
@@ -29,14 +35,17 @@ typedef mrb_int mrb_jmpbuf_impl;
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define MRB_SETJMP _setjmp
#define MRB_LONGJMP _longjmp
+#elif defined(__MINGW64__) && defined(__GNUC__) && __GNUC__ >= 4
+#define MRB_SETJMP __builtin_setjmp
+#define MRB_LONGJMP __builtin_longjmp
#else
#define MRB_SETJMP setjmp
#define MRB_LONGJMP longjmp
#endif
-#define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) {
+#define MRB_TRY(buf) if (MRB_SETJMP((buf)->impl) == 0) {
#define MRB_CATCH(buf) } else {
-#define MRB_END_EXC(buf) } } while(0)
+#define MRB_END_EXC(buf) }
#define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1);
#define mrb_jmpbuf_impl jmp_buf
@@ -46,9 +55,11 @@ typedef mrb_int mrb_jmpbuf_impl;
struct mrb_jmpbuf {
mrb_jmpbuf_impl impl;
-#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
+#if defined(MRB_USE_CXX_EXCEPTION)
static mrb_int jmpbuf_id;
+# if defined(__cplusplus)
mrb_jmpbuf() : impl(jmpbuf_id++) {}
+# endif
#endif
};
diff --git a/include/mruby/value.h b/include/mruby/value.h
index f988826ca..d65da30fb 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -1,5 +1,5 @@
-/*
-** mruby/value.h - mruby value definitions
+/**
+** @file mruby/value.h - mruby value definitions
**
** See Copyright Notice in mruby.h
*/
@@ -9,28 +9,66 @@
#include "common.h"
-/**
+/*
* MRuby Value definition functions and macros.
*/
MRB_BEGIN_DECL
+/**
+ * mruby Symbol.
+ * @class mrb_sym
+ *
+ * You can create an mrb_sym by simply using mrb_str_intern() or mrb_intern_cstr()
+ */
typedef uint32_t mrb_sym;
+
+/**
+ * mruby Boolean.
+ * @class mrb_bool
+ *
+ *
+ * Used internally to represent boolean. Can be TRUE or FALSE.
+ * Not to be confused with Ruby's boolean classes, which can be
+ * obtained using mrb_false_value() and mrb_true_value()
+ */
+#if defined(__cplusplus) || (defined(__bool_true_false_are_defined) && __bool_true_false_are_defined)
+typedef bool mrb_bool;
+
+# ifndef FALSE
+# define FALSE false
+# endif
+# ifndef TRUE
+# define TRUE true
+# endif
+#else
+# if __STDC_VERSION__ >= 199901L
+typedef _Bool mrb_bool;
+# else
typedef uint8_t mrb_bool;
-struct mrb_state;
+# endif
-#if defined(MRB_INT16) && defined(MRB_INT64)
-# error "You can't define MRB_INT16 and MRB_INT64 at the same time."
+# ifndef FALSE
+# define FALSE 0
+# endif
+# ifndef TRUE
+# define TRUE 1
+# endif
#endif
+struct mrb_state;
+
#if defined _MSC_VER && _MSC_VER < 1800
# define PRIo64 "llo"
# define PRId64 "lld"
+# define PRIu64 "llu"
# define PRIx64 "llx"
# define PRIo16 "ho"
# define PRId16 "hd"
+# define PRIu16 "hu"
# define PRIx16 "hx"
# define PRIo32 "o"
# define PRId32 "d"
+# define PRIu32 "u"
# define PRIx32 "x"
#else
# include <inttypes.h>
@@ -38,34 +76,34 @@ struct mrb_state;
#if defined(MRB_INT64)
typedef int64_t mrb_int;
+ typedef uint64_t mrb_uint;
# 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_INT_MIN INT64_MIN
+# define MRB_INT_MAX INT64_MAX
# 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;
+ typedef uint32_t mrb_uint;
# 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_INT_MIN INT32_MIN
+# define MRB_INT_MAX INT32_MAX
# define MRB_PRIo PRIo32
# define MRB_PRId PRId32
# define MRB_PRIx PRIx32
#endif
+#ifdef MRB_ENDIAN_BIG
+# define MRB_ENDIAN_LOHI(a,b) a b
+#else
+# define MRB_ENDIAN_LOHI(a,b) b a
+#endif
-#ifndef MRB_WITHOUT_FLOAT
+MRB_API mrb_int mrb_int_read(const char *p, const char *e, char **endp);
+#ifndef MRB_NO_FLOAT
MRB_API double mrb_float_read(const char*, char**);
-#ifdef MRB_USE_FLOAT
+#ifdef MRB_USE_FLOAT32
typedef float mrb_float;
#else
typedef double mrb_float;
@@ -73,15 +111,12 @@ MRB_API double mrb_float_read(const char*, char**);
#endif
#if defined _MSC_VER && _MSC_VER < 1900
-# ifndef __cplusplus
-# define inline __inline
-# endif
# include <stdarg.h>
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
+# if _MSC_VER < 1800 && !defined MRB_NO_FLOAT
# include <float.h>
# define isfinite(n) _finite(n)
# define isnan _isnan
@@ -93,35 +128,51 @@ static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
# endif
#endif
+#define MRB_VTYPE_FOREACH(f) \
+ /* mrb_vtype */ /* c type */ /* ruby class */ \
+ f(MRB_TT_FALSE, void, "false") \
+ f(MRB_TT_TRUE, void, "true") \
+ f(MRB_TT_SYMBOL, void, "Symbol") \
+ f(MRB_TT_UNDEF, void, "undefined") \
+ f(MRB_TT_FREE, void, "free") \
+ f(MRB_TT_FLOAT, struct RFloat, "Float") \
+ f(MRB_TT_INTEGER, struct RInteger, "Integer") \
+ f(MRB_TT_CPTR, struct RCptr, "cptr") \
+ f(MRB_TT_OBJECT, struct RObject, "Object") \
+ f(MRB_TT_CLASS, struct RClass, "Class") \
+ f(MRB_TT_MODULE, struct RClass, "Module") \
+ f(MRB_TT_ICLASS, struct RClass, "iClass") \
+ f(MRB_TT_SCLASS, struct RClass, "SClass") \
+ f(MRB_TT_PROC, struct RProc, "Proc") \
+ f(MRB_TT_ARRAY, struct RArray, "Array") \
+ f(MRB_TT_HASH, struct RHash, "Hash") \
+ f(MRB_TT_STRING, struct RString, "String") \
+ f(MRB_TT_RANGE, struct RRange, "Range") \
+ f(MRB_TT_EXCEPTION, struct RException, "Exception") \
+ f(MRB_TT_ENV, struct REnv, "env") \
+ f(MRB_TT_DATA, struct RData, "Data") \
+ f(MRB_TT_FIBER, struct RFiber, "Fiber") \
+ f(MRB_TT_ISTRUCT, struct RIStruct, "istruct") \
+ f(MRB_TT_BREAK, struct RBreak, "break") \
+ f(MRB_TT_COMPLEX, struct RComplex, "Complex") \
+ f(MRB_TT_RATIONAL, struct RRational, "Rational")
+
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 */
+#define MRB_VTYPE_DEFINE(tt, type, name) tt,
+ MRB_VTYPE_FOREACH(MRB_VTYPE_DEFINE)
+#undef MRB_VTYPE_DEFINE
+ MRB_TT_MAXDEFINE
};
+#define MRB_VTYPE_TYPEOF(tt) MRB_TYPEOF_##tt
+
+#define MRB_VTYPE_TYPEDEF(tt, type, name) typedef type MRB_VTYPE_TYPEOF(tt);
+MRB_VTYPE_FOREACH(MRB_VTYPE_TYPEDEF)
+#undef MRB_VTYPE_TYPEDEF
+
+/* for compatibility */
+#define MRB_TT_FIXNUM MRB_TT_INTEGER
+
#include <mruby/object.h>
#ifdef MRB_DOCUMENTATION_BLOCK
@@ -140,6 +191,13 @@ typedef void mrb_value;
#endif
+#if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_64BIT))
+struct RCptr {
+ MRB_OBJECT_HEADER;
+ void *p;
+};
+#endif
+
#if defined(MRB_NAN_BOXING)
#include "boxing_nan.h"
#elif defined(MRB_WORD_BOXING)
@@ -148,8 +206,25 @@ typedef void mrb_value;
#include "boxing_no.h"
#endif
+#if INTPTR_MAX < MRB_INT_MAX
+ typedef intptr_t mrb_ssize;
+# define MRB_SSIZE_MAX INTPTR_MAX
+#else
+ typedef mrb_int mrb_ssize;
+# define MRB_SSIZE_MAX MRB_INT_MAX
+#endif
+
+#ifndef mrb_immediate_p
+#define mrb_immediate_p(o) (mrb_type(o) <= MRB_TT_CPTR)
+#endif
+#ifndef mrb_integer_p
+#define mrb_integer_p(o) (mrb_type(o) == MRB_TT_INTEGER)
+#endif
#ifndef mrb_fixnum_p
-#define mrb_fixnum_p(o) (mrb_type(o) == MRB_TT_FIXNUM)
+#define mrb_fixnum_p(o) mrb_integer_p(o)
+#endif
+#ifndef mrb_symbol_p
+#define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL)
#endif
#ifndef mrb_undef_p
#define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF)
@@ -157,25 +232,82 @@ typedef void mrb_value;
#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)
+#ifndef mrb_false_p
+#define mrb_false_p(o) (mrb_type(o) == MRB_TT_FALSE && !!mrb_fixnum(o))
+#endif
+#ifndef mrb_true_p
+#define mrb_true_p(o) (mrb_type(o) == MRB_TT_TRUE)
#endif
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
+#ifndef mrb_float_p
#define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT)
#endif
-#define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL)
+#endif
+#ifndef mrb_array_p
#define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY)
+#endif
+#ifndef mrb_string_p
#define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING)
+#endif
+#ifndef mrb_hash_p
#define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH)
+#endif
+#ifndef mrb_cptr_p
#define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR)
+#endif
+#ifndef mrb_exception_p
#define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION)
+#endif
+#ifndef mrb_free_p
+#define mrb_free_p(o) (mrb_type(o) == MRB_TT_FREE)
+#endif
+#ifndef mrb_object_p
+#define mrb_object_p(o) (mrb_type(o) == MRB_TT_OBJECT)
+#endif
+#ifndef mrb_class_p
+#define mrb_class_p(o) (mrb_type(o) == MRB_TT_CLASS)
+#endif
+#ifndef mrb_module_p
+#define mrb_module_p(o) (mrb_type(o) == MRB_TT_MODULE)
+#endif
+#ifndef mrb_iclass_p
+#define mrb_iclass_p(o) (mrb_type(o) == MRB_TT_ICLASS)
+#endif
+#ifndef mrb_sclass_p
+#define mrb_sclass_p(o) (mrb_type(o) == MRB_TT_SCLASS)
+#endif
+#ifndef mrb_proc_p
+#define mrb_proc_p(o) (mrb_type(o) == MRB_TT_PROC)
+#endif
+#ifndef mrb_range_p
+#define mrb_range_p(o) (mrb_type(o) == MRB_TT_RANGE)
+#endif
+#ifndef mrb_env_p
+#define mrb_env_p(o) (mrb_type(o) == MRB_TT_ENV)
+#endif
+#ifndef mrb_data_p
+#define mrb_data_p(o) (mrb_type(o) == MRB_TT_DATA)
+#endif
+#ifndef mrb_fiber_p
+#define mrb_fiber_p(o) (mrb_type(o) == MRB_TT_FIBER)
+#endif
+#ifndef mrb_istruct_p
+#define mrb_istruct_p(o) (mrb_type(o) == MRB_TT_ISTRUCT)
+#endif
+#ifndef mrb_break_p
+#define mrb_break_p(o) (mrb_type(o) == MRB_TT_BREAK)
+#endif
+#ifndef mrb_bool
+#define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE)
+#endif
#define mrb_test(o) mrb_bool(o)
-MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value);
-/*
+/**
* Returns a float in Ruby.
+ *
+ * Takes a float and boxes it into an mrb_value
*/
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
{
mrb_value v;
@@ -185,7 +317,7 @@ MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
}
#endif
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_cptr_value(struct mrb_state *mrb, void *p)
{
mrb_value v;
@@ -194,17 +326,24 @@ mrb_cptr_value(struct mrb_state *mrb, void *p)
return v;
}
-/*
- * Returns a fixnum in Ruby.
+/**
+ * Returns an integer in Ruby.
*/
+MRB_INLINE mrb_value mrb_int_value(struct mrb_state *mrb, mrb_int i)
+{
+ mrb_value v;
+ SET_INT_VALUE(mrb, v, i);
+ return v;
+}
+
MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i)
{
mrb_value v;
- SET_INT_VALUE(v, i);
+ SET_FIXNUM_VALUE(v, i);
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_symbol_value(mrb_sym i)
{
mrb_value v;
@@ -212,7 +351,7 @@ mrb_symbol_value(mrb_sym i)
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_obj_value(void *p)
{
mrb_value v;
@@ -222,8 +361,7 @@ mrb_obj_value(void *p)
return v;
}
-
-/*
+/**
* Get a nil mrb_value object.
*
* @return
@@ -236,7 +374,7 @@ MRB_INLINE mrb_value mrb_nil_value(void)
return v;
}
-/*
+/**
* Returns false in Ruby.
*/
MRB_INLINE mrb_value mrb_false_value(void)
@@ -246,7 +384,7 @@ MRB_INLINE mrb_value mrb_false_value(void)
return v;
}
-/*
+/**
* Returns true in Ruby.
*/
MRB_INLINE mrb_value mrb_true_value(void)
@@ -256,7 +394,7 @@ MRB_INLINE mrb_value mrb_true_value(void)
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_bool_value(mrb_bool boolean)
{
mrb_value v;
@@ -264,7 +402,7 @@ mrb_bool_value(mrb_bool boolean)
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_undef_value(void)
{
mrb_value v;
@@ -272,34 +410,25 @@ mrb_undef_value(void)
return v;
}
-#ifdef MRB_USE_ETEXT_EDATA
-#if (defined(__APPLE__) && defined(__MACH__))
-#include <mach-o/getsect.h>
-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[];
+#if defined(MRB_USE_ETEXT_EDATA) && !defined(MRB_USE_LINK_TIME_RO_DATA_P)
+# ifdef __GNUC__
+# warning MRB_USE_ETEXT_EDATA is deprecated. Define MRB_USE_LINK_TIME_RO_DATA_P instead.
+# endif
+# define MRB_USE_LINK_TIME_RO_DATA_P
+#endif
-static inline mrb_bool
-mrb_ro_data_p(const char *p)
-{
- return _etext < p && p < _edata;
-}
-#else
+#if defined(MRB_USE_CUSTOM_RO_DATA_P)
+/* If you define `MRB_USE_CUSTOM_RO_DATA_P`, you must implement `mrb_ro_data_p()`. */
+mrb_bool mrb_ro_data_p(const char *p);
+#elif defined(MRB_USE_LINK_TIME_RO_DATA_P)
+extern char __ehdr_start[];
extern char __init_array_start[];
static inline mrb_bool
mrb_ro_data_p(const char *p)
{
- return _etext < p && p < (char*)&__init_array_start;
+ return __ehdr_start < p && p < __init_array_start;
}
-#endif
-#endif
#else
# define mrb_ro_data_p(p) FALSE
#endif
diff --git a/include/mruby/variable.h b/include/mruby/variable.h
index a0fbca1f9..3dc2c9a6d 100644
--- a/include/mruby/variable.h
+++ b/include/mruby/variable.h
@@ -1,5 +1,5 @@
-/*
-** mruby/variable.h - mruby variables
+/**
+** @file mruby/variable.h - mruby variables
**
** See Copyright Notice in mruby.h
*/
@@ -14,27 +14,13 @@
*/
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);
+size_t mrb_obj_iv_tbl_memsize(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);
@@ -97,18 +83,15 @@ MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
*
* 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);
@@ -117,12 +100,14 @@ MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_
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*);
+void mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
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);
+mrb_bool mrb_ident_p(const char *s, mrb_int len);
/* GC functions */
void mrb_gc_mark_gv(mrb_state*);
@@ -131,6 +116,10 @@ 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*);
+/* return non zero to break the loop */
+typedef int (mrb_iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
+MRB_API void mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p);
+
MRB_END_DECL
#endif /* MRUBY_VARIABLE_H */
diff --git a/include/mruby/version.h b/include/mruby/version.h
index 43d6461ea..185263f32 100644
--- a/include/mruby/version.h
+++ b/include/mruby/version.h
@@ -1,5 +1,5 @@
-/*
-** mruby/version.h - mruby version definition
+/**
+** @file mruby/version.h - mruby version definition
**
** See Copyright Notice in mruby.h
*/
@@ -27,7 +27,7 @@ MRB_BEGIN_DECL
/*
* The version of Ruby used by mruby.
*/
-#define MRUBY_RUBY_VERSION "1.9"
+#define MRUBY_RUBY_VERSION "3.0"
/*
* Ruby engine.
@@ -37,17 +37,35 @@ MRB_BEGIN_DECL
/*
* Major release version number.
*/
-#define MRUBY_RELEASE_MAJOR 1
+#define MRUBY_RELEASE_MAJOR 3
/*
* Minor release version number.
*/
-#define MRUBY_RELEASE_MINOR 4
+#define MRUBY_RELEASE_MINOR 0
/*
* Tiny release version number.
*/
-#define MRUBY_RELEASE_TEENY 1
+#define MRUBY_RELEASE_TEENY 0
+
+/*
+ * Patch level.
+ */
+#define MRUBY_PATCHLEVEL -1
+
+/*
+ * Patch level string. (optional)
+ */
+#define MRUBY_PATCHLEVEL_STR ""
+
+#ifndef MRUBY_PATCHLEVEL_STR
+# if MRUBY_PATCHLEVEL < 0
+# define MRUBY_PATCHLEVEL_STR "dev"
+# else
+# define MRUBY_PATCHLEVEL_STR "p"MRB_STRINGIZE(MRUBY_PATCHLEVEL)
+# endif
+#endif
/*
* The mruby version.
@@ -62,22 +80,36 @@ MRB_BEGIN_DECL
/*
* Release year.
*/
-#define MRUBY_RELEASE_YEAR 2018
+#define MRUBY_RELEASE_YEAR 2021
/*
* Release month.
*/
-#define MRUBY_RELEASE_MONTH 4
+#define MRUBY_RELEASE_MONTH 3
/*
* Release day.
*/
-#define MRUBY_RELEASE_DAY 27
+#define MRUBY_RELEASE_DAY 5
/*
* Release date as a string.
*/
-#define MRUBY_RELEASE_DATE MRB_STRINGIZE(MRUBY_RELEASE_YEAR) "-" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) "-" MRB_STRINGIZE(MRUBY_RELEASE_DAY)
+#define MRUBY_RELEASE_DATE \
+ MRUBY_RELEASE_YEAR_STR "-" \
+ MRUBY_RELEASE_MONTH_STR "-" \
+ MRUBY_RELEASE_DAY_STR
+#define MRUBY_RELEASE_YEAR_STR MRB_STRINGIZE(MRUBY_RELEASE_YEAR)
+#if MRUBY_RELEASE_MONTH < 10
+#define MRUBY_RELEASE_MONTH_STR "0" MRB_STRINGIZE(MRUBY_RELEASE_MONTH)
+#else
+#define MRUBY_RELEASE_MONTH_STR MRB_STRINGIZE(MRUBY_RELEASE_MONTH)
+#endif
+#if MRUBY_RELEASE_DAY < 10
+#define MRUBY_RELEASE_DAY_STR "0" MRB_STRINGIZE(MRUBY_RELEASE_DAY)
+#else
+#define MRUBY_RELEASE_DAY_STR MRB_STRINGIZE(MRUBY_RELEASE_DAY)
+#endif
/*
* The year mruby was first created.
@@ -92,9 +124,10 @@ MRB_BEGIN_DECL
/*
* mruby's version, and release date.
*/
-#define MRUBY_DESCRIPTION \
- "mruby " MRUBY_VERSION \
- " (" MRUBY_RELEASE_DATE ") " \
+#define MRUBY_DESCRIPTION \
+ "mruby " MRUBY_VERSION \
+ MRUBY_PATCHLEVEL_STR \
+ " (" MRUBY_RELEASE_DATE ")" \
/*
* mruby's copyright information.
diff --git a/lib/mruby-core-ext.rb b/lib/mruby-core-ext.rb
deleted file mode 100644
index 4c6d3ca76..000000000
--- a/lib/mruby-core-ext.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-class 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
diff --git a/lib/mruby/build.rb b/lib/mruby/build.rb
index 047ae13dc..67267ab46 100644
--- a/lib/mruby/build.rb
+++ b/lib/mruby/build.rb
@@ -1,7 +1,12 @@
+require "mruby/core_ext"
require "mruby/build/load_gems"
require "mruby/build/command"
module MRuby
+ autoload :Gem, "mruby/gem"
+ autoload :Lockfile, "mruby/lockfile"
+ autoload :Presym, "mruby/presym"
+
class << self
def targets
@targets ||= {}
@@ -18,61 +23,85 @@ module MRuby
class Toolchain
class << self
attr_accessor :toolchains
+
+ def guess
+ if cc = ENV["CC"] || ENV["CXX"]
+ return "clang" if cc.include?("clang")
+ else
+ return "clang" if RUBY_PLATFORM =~ /darwin|(?:free|open)bsd/
+ return "gcc" if RUBY_PLATFORM.include?("cygwin")
+ return "visualcpp" if ENV.include?("VisualStudioVersion")
+ return "visualcpp" if ENV.include?("VSINSTALLDIR")
+ end
+ "gcc"
+ end
end
def initialize(name, &block)
@name, @initializer = name.to_s, block
- MRuby::Toolchain.toolchains ||= {}
MRuby::Toolchain.toolchains[@name] = self
end
- def setup(conf,params={})
+ 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
+ self.toolchains = {}
end
- Toolchain.load
class Build
class << self
attr_accessor :current
+
+ def mruby_config_path
+ path = ENV['MRUBY_CONFIG'] || ENV['CONFIG']
+ if path.nil? || path.empty?
+ path = "#{MRUBY_ROOT}/build_config/default.rb"
+ elsif !File.file?(path) && !Pathname.new(path).absolute?
+ f = "#{MRUBY_ROOT}/build_config/#{path}.rb"
+ path = File.exist?(f) ? f : File.extname(path).empty? ? f : path
+ end
+ path
+ end
+
+ def install_dir
+ @install_dir ||= ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin"
+ end
end
+
include Rake::DSL
include LoadGems
- attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir
- attr_reader :libmruby, :gems, :toolchains
- attr_writer :enable_bintest, :enable_test
+ attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir, :defines
+ attr_reader :products, :libmruby_core_objs, :libmruby_objs, :gems, :toolchains, :presym, :mrbc_build, :gem_dir_to_repo_url
+
+ 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)
+ Exts = Struct.new(:object, :executable, :library, :presym_preprocessed)
- def initialize(name='host', build_dir=nil, &block)
+ def initialize(name='host', build_dir=nil, internal: false, &block)
@name = name.to_s
- unless MRuby.targets[@name]
+ unless current = MRuby.targets[@name]
if ENV['OS'] == 'Windows_NT'
- @exts = Exts.new('.o', '.exe', '.a')
+ @exts = Exts.new('.o', '.exe', '.a', '.pi')
else
- @exts = Exts.new('.o', '', '.a')
+ @exts = Exts.new('.o', '', '.a', '.pi')
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))
+ @gem_clone_dir = "#{build_dir}/repos/#{@name}"
+ @defines = []
+ @cc = Command::Compiler.new(self, %w(.c), label: "CC")
+ @cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp), label: "CXX")
+ @objc = Command::Compiler.new(self, %w(.m), label: "OBJC")
+ @asm = Command::Compiler.new(self, %w(.S .asm .s), label: "ASM")
@linker = Command::Linker.new(self)
@archiver = Command::Archiver.new(self)
@yacc = Command::Yacc.new(self)
@@ -80,24 +109,53 @@ module MRuby
@git = Command::Git.new(self)
@mrbc = Command::Mrbc.new(self)
+ @products = []
@bins = []
- @gems, @libmruby = MRuby::Gem::List.new, []
+ @gems = MRuby::Gem::List.new
+ @libmruby_core_objs = []
+ @libmruby_objs = [@libmruby_core_objs]
+ @enable_libmruby = true
@build_mrbtest_lib_only = false
@cxx_exception_enabled = false
@cxx_exception_disabled = false
@cxx_abi_enabled = false
@enable_bintest = false
@enable_test = false
+ @enable_lock = true
+ @enable_presym = true
+ @mrbcfile_external = false
+ @internal = internal
@toolchains = []
+ @gem_dir_to_repo_url = {}
+
+ MRuby.targets[@name] = current = self
+ end
- MRuby.targets[@name] = self
+ MRuby::Build.current = current
+ begin
+ current.instance_eval(&block)
+ ensure
+ if current.libmruby_enabled? && !current.mrbcfile_external?
+ if current.presym_enabled?
+ current.create_mrbc_build if current.host? || current.gems["mruby-bin-mrbc"]
+ elsif current.host?
+ current.build_mrbc_exec
+ end
+ end
+ current.presym = Presym.new(current) if current.presym_enabled?
end
+ end
- MRuby::Build.current = MRuby.targets[@name]
- MRuby.targets[@name].instance_eval(&block)
+ def libmruby_enabled?
+ @enable_libmruby
+ end
+
+ def disable_libmruby
+ @enable_libmruby = false
+ end
- build_mrbc_exec if name == 'host'
- build_mrbtest if test_enabled?
+ def debug_enabled?
+ @enable_debug
end
def enable_debug
@@ -108,6 +166,27 @@ module MRuby
end
end
@mrbc.compile_options += ' -g'
+
+ @enable_debug = true
+ end
+
+ def presym_enabled?
+ @enable_presym
+ end
+
+ def disable_presym
+ if @enable_presym
+ @enable_presym = false
+ compilers.each{|c| c.defines << "MRB_NO_PRESYM"}
+ end
+ end
+
+ def disable_lock
+ @enable_lock = false
+ end
+
+ def lock_enabled?
+ Lockfile.enabled? && @enable_lock
end
def disable_cxx_exception
@@ -125,7 +204,7 @@ module MRuby
end
@cxx_exception_enabled = true
compilers.each { |c|
- c.defines += %w(MRB_ENABLE_CXX_EXCEPTION)
+ c.defines += %w(MRB_USE_CXX_EXCEPTION)
c.flags << c.cxx_exception_flag
}
linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
@@ -145,39 +224,54 @@ module MRuby
raise "cxx_exception already enabled"
end
compilers.each { |c|
- c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI)
+ c.defines += %w(MRB_USE_CXX_EXCEPTION MRB_USE_CXX_ABI)
c.flags << c.cxx_compile_flag
+ c.flags = c.flags.flatten - c.cxx_invalid_flags.flatten
}
- 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?
+ def compile_as_cxx(src, cxx_src = nil, obj = nil, includes = [])
+ #
+ # If `cxx_src` is specified, this method behaves the same as before as
+ # compatibility mode, but `.d` file is not read.
+ #
+ # If `cxx_src` is omitted, `.d` file is read by using mruby standard
+ # Rake rule (C++ source name is also changed).
+ #
+ if cxx_src
+ obj ||= cxx_src + @exts.object
+ dsts = [obj]
+ dsts << (cxx_src + @exts.presym_preprocessed) if presym_enabled?
+ defines = []
+ include_paths = ["#{MRUBY_ROOT}/src", *includes]
+ dsts.each do |dst|
+ file dst => cxx_src do |t|
+ cxx.run t.name, t.prerequisites.first, defines, include_paths
+ end
+ end
+ else
+ cxx_src = "#{build_dir}/#{src.relative_path.to_s.remove_leading_parents}".ext << "-cxx.cxx"
+ obj = cxx_src.ext(@exts.object)
+ end
file cxx_src => [src, __FILE__] do |t|
- FileUtils.mkdir_p File.dirname t.name
+ mkdir_p File.dirname t.name
IO.write t.name, <<EOS
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
-#ifndef MRB_ENABLE_CXX_ABI
+#ifndef MRB_USE_CXX_ABI
extern "C" {
#endif
-#include "#{src}"
-#ifndef MRB_ENABLE_CXX_ABI
+#include "#{File.absolute_path src}"
+#ifndef MRB_USE_CXX_ABI
}
#endif
EOS
end
- file obj => cxx_src do |t|
- cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes
- end
-
obj
end
@@ -189,11 +283,16 @@ EOS
@enable_bintest
end
- def toolchain(name, params={})
- tc = Toolchain.toolchains[name.to_s]
- fail "Unknown #{name} toolchain" unless tc
+ def toolchain(name=Toolchain.guess, params={})
+ name = name.to_s
+ tc = Toolchain.toolchains[name] || begin
+ path = "#{MRUBY_ROOT}/tasks/toolchains/#{name}.rake"
+ fail "Unknown #{name} toolchain" unless File.exist?(path)
+ load path
+ Toolchain.toolchains[name]
+ end
tc.setup(self, params)
- @toolchains.unshift name.to_s
+ @toolchains.unshift name
end
def primary_toolchain
@@ -207,25 +306,43 @@ EOS
def enable_test
@enable_test = true
end
+ alias build_mrbtest enable_test
def test_enabled?
@enable_test
end
- def build_mrbtest
- gem :core => 'mruby-test'
+ def build_mrbc_exec
+ gem :core => 'mruby-bin-mrbc' unless @gems['mruby-bin-mrbc']
end
- def build_mrbc_exec
- gem :core => 'mruby-bin-mrbc'
+ def locks
+ Lockfile.build(@name)
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")
+ gem_name = "mruby-bin-mrbc"
+ if (gem = @gems[gem_name])
+ @mrbcfile = exefile("#{gem.build.build_dir}/bin/mrbc")
+ elsif !host? && (host = MRuby.targets["host"])
+ if (gem = host.gems[gem_name])
+ @mrbcfile = exefile("#{gem.build.build_dir}/bin/mrbc")
+ elsif host.mrbcfile_external?
+ @mrbcfile = host.mrbcfile
+ end
+ end
+ @mrbcfile || fail("external mrbc or mruby-bin-mrbc gem in current('#{@name}') or 'host' build is required")
+ end
+
+ def mrbcfile=(path)
+ @mrbcfile = path
+ @mrbcfile_external = true
+ end
+
+ def mrbcfile_external?
+ @mrbcfile_external
end
def compilers
@@ -236,36 +353,43 @@ EOS
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__), '..', '..'))
+ compiler.defines << "MRB_NO_GEMS" unless enable_gems? && libmruby_enabled?
+ end
+ [@cc, *(@cxx if cxx_exception_enabled?)].each do |compiler|
+ compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.object)
+ compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.presym_preprocessed) if presym_enabled?
end
end
- def filename(name)
- if name.is_a?(Array)
- name.flatten.map { |n| filename(n) }
- else
- '"%s"' % name.gsub('/', file_separator)
+ def define_installer(src)
+ dst = "#{self.class.install_dir}/#{File.basename(src)}"
+ file dst => src do
+ install_D src, dst
end
+ dst
+ end
+
+ def define_installer_if_needed(bin)
+ exe = exefile("#{build_dir}/bin/#{bin}")
+ host? ? define_installer(exe) : exe
end
- def cygwin_filename(name)
+ def filename(name)
if name.is_a?(Array)
- name.flatten.map { |n| cygwin_filename(n) }
+ name.flatten.map { |n| filename(n) }
else
- '"%s"' % `cygpath -w "#{filename(name)}"`.strip
+ name.gsub('/', file_separator)
end
end
def exefile(name)
if name.is_a?(Array)
name.flatten.map { |n| exefile(n) }
- else
+ elsif File.extname(name).empty?
"#{name}#{exts.executable}"
+ else
+ # `name` sometimes have (non-standard) extension (e.g. `.bat`).
+ name
end
end
@@ -294,7 +418,7 @@ EOS
end
def verbose_flag
- $verbose ? ' -v' : ''
+ Rake.verbose ? ' -v' : ''
end
def run_test
@@ -305,9 +429,12 @@ EOS
end
def run_bintest
+ puts ">>> Bintest #{name} <<<"
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 ' '}"
+ mrbc = @gems["mruby-bin-mrbc"] ? exefile("#{@build_dir}/bin/mrbc") : mrbcfile
+ env = {"BUILD_DIR" => @build_dir, "MRBCFILE" => mrbc}
+ sh env, "ruby test/bintest.rb#{verbose_flag} #{targets.join ' '}"
end
def print_build_summary
@@ -317,7 +444,8 @@ EOS
puts " Binaries: #{@bins.join(', ')}" unless @bins.empty?
unless @gems.empty?
puts " Included Gems:"
- @gems.map do |gem|
+ gems = @gems.sort_by { |gem| gem.name }
+ gems.each 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}"
@@ -327,6 +455,54 @@ EOS
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
+
+ def host?
+ @name == "host"
+ end
+
+ def internal?
+ @internal
+ end
+
+ protected
+
+ attr_writer :presym
+
+ def create_mrbc_build
+ exclusions = %i[@name @build_dir @gems @enable_test @enable_bintest @internal]
+ name = "#{@name}/mrbc"
+ MRuby.targets.delete(name)
+ build = self.class.new(name, internal: true){}
+ build.build_dir = "#{@build_dir}/mrbc"
+ instance_variables.each do |n|
+ next if exclusions.include?(n)
+ v = instance_variable_get(n)
+ v = case v
+ when nil, true, false, Numeric; v
+ when String, Command; v.clone
+ else Marshal.load(Marshal.dump(v)) # deep clone
+ end
+ build.instance_variable_set(n, v)
+ end
+ build.build_mrbc_exec
+ build.disable_libmruby
+ build.disable_presym
+ @mrbc_build = build
+ self.mrbcfile = build.mrbcfile
+ build
+ end
end # Build
class CrossBuild < Build
@@ -337,17 +513,25 @@ EOS
attr_accessor :host_target, :build_target
def initialize(name, build_dir=nil, &block)
- @endian = nil
@test_runner = Command::CrossTestRunner.new(self)
super
+ unless mrbcfile_external? || MRuby.targets['host']
+ # add minimal 'host'
+ MRuby::Build.new('host') do |conf|
+ conf.toolchain
+ conf.build_mrbc_exec
+ conf.disable_libmruby
+ conf.disable_presym
+ end
+ end
end
def mrbcfile
- MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/mrbc")
+ mrbcfile_external? ? super : MRuby::targets['host'].mrbcfile
end
def run_test
- @test_runner.runner_options << ' -v' if $verbose
+ @test_runner.runner_options << verbose_flag
mrbtest = exefile("#{build_dir}/bin/mrbtest")
if (@test_runner.command == nil)
puts "You should run #{mrbtest} on target device."
@@ -357,25 +541,8 @@ EOS
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
+ protected
- def little_endian
- if @endian
- puts "Endian has already specified as #{@endian}."
- return
- end
- @endian = :little
- @mrbc.compile_options += ' -e'
- end
+ def create_mrbc_build; end
end # CrossBuild
end # MRuby
diff --git a/lib/mruby/build/command.rb b/lib/mruby/build/command.rb
index 694b4a24c..c93d08ea7 100644
--- a/lib/mruby/build/command.rb
+++ b/lib/mruby/build/command.rb
@@ -4,7 +4,7 @@ module MRuby
class Command
include Rake::DSL
extend Forwardable
- def_delegators :@build, :filename, :objfile, :libfile, :exefile, :cygwin_filename
+ def_delegators :@build, :filename, :objfile, :libfile, :exefile
attr_accessor :build, :command
def initialize(build)
@@ -24,38 +24,47 @@ module MRuby
target
end
- NotFoundCommands = {}
+ def shellquote(s)
+ if ENV['OS'] == 'Windows_NT'
+ "\"#{s}\""
+ else
+ "#{s}"
+ end
+ end
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
+ sh "#{build.filename(command)} #{options % params}"
end
end
class Command::Compiler < Command
- attr_accessor :flags, :include_paths, :defines, :source_exts
+ attr_accessor :label, :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
+ attr_accessor :cxx_compile_flag, :cxx_exception_flag, :cxx_invalid_flags
+ attr_writer :preprocess_options
- def initialize(build, source_exts=[])
+ def initialize(build, source_exts=[], label: "CC")
super(build)
@command = ENV['CC'] || 'cc'
+ @label = label
@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}'
+ @defines = []
+ @option_include_path = %q[-I"%s"]
+ @option_define = %q[-D"%s"]
+ @compile_options = %q[%{flags} -o "%{outfile}" -c "%{infile}"]
+ @cxx_invalid_flags = []
+ @out_ext = build.exts.object
end
alias header_search_paths include_paths
+
+ def preprocess_options
+ @preprocess_options ||= @compile_options.sub(/(?:\A|\s)\K-c(?=\s)/, "-E -P")
+ end
+
def search_header_path(name)
header_search_paths.find do |v|
File.exist? build.filename("#{v}/#{name}").sub(/^"(.*)"$/, '\1')
@@ -67,39 +76,37 @@ module MRuby
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 }
+ def all_flags(_defines=[], _include_paths=[], _flags=[])
+ define_flags = [defines, _defines, build.defines].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
+ option_include_path % filename(f)
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) }
+ def run(outfile, infile, _defines=[], _include_paths=[], _flags=[])
+ mkdir_p File.dirname(outfile)
+ flags = all_flags(_defines, _include_paths, _flags)
+ if object_ext?(outfile)
+ label = @label
+ opts = compile_options
else
- _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags),
- :infile => filename(infile), :outfile => filename(outfile) }
+ label = "CPP"
+ opts = preprocess_options
+ flags << " -DMRB_PRESYM_SCANNING"
end
+ _pp label, infile.relative_path, outfile.relative_path
+ _run opts, flags: flags, infile: filename(infile), outfile: filename(outfile)
end
- def define_rules(build_dir, source_dir='')
- @out_ext = build.exts.object
+ 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}$")
+ generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbc/)(.*)#{Regexp.escape out_ext}$")
else
- generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbgems/.+/)(.*)#{Regexp.escape out_ext}$")
+ generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbc/|mrbgems/.+/)(.*)#{Regexp.escape out_ext}$")
end
source_exts.each do |ext, compile|
rule generated_file_matcher => [
@@ -127,13 +134,47 @@ module MRuby
end
private
+
+ #
+ # === Example of +.d+ file
+ #
+ # ==== Without <tt>-MP</tt> compiler flag
+ #
+ # /build/host/src/array.o: /src/array.c \
+ # /include/mruby/common.h /include/mruby/value.h \
+ # /src/value_array.h
+ #
+ # ==== With <tt>-MP</tt> compiler flag
+ #
+ # /build/host/src/array.o: /src/array.c \
+ # /include/mruby/common.h /include/mruby/value.h \
+ # /src/value_array.h
+ #
+ # /include/mruby/common.h:
+ #
+ # /include/mruby/value.h:
+ #
+ # /src/value_array.h:
+ #
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 ]
+ dep_file = file.ext(".d")
+ return [MRUBY_CONFIG] unless object_ext?(file) && File.exist?(dep_file)
+
+ deps = File.read(dep_file).gsub("\\\n ", "").split("\n").map do |dep_line|
+ # dep_line:
+ # - "/build/host/src/array.o: /src/array.c /include/mruby/common.h ..."
+ # - ""
+ # - "/include/mruby/common.h:"
+ dep_line.scan(/^\S+:\s+(.+)$/).flatten.map { |s| s.split(' ') }.flatten
+ # => ["/src/array.c", "/include/mruby/common.h" , ...]
+ # []
+ # []
+ end.flatten.uniq
+ deps << MRUBY_CONFIG
+ end
+
+ def object_ext?(path)
+ File.extname(path) == build.exts.object
end
end
@@ -148,18 +189,14 @@ module MRuby
@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}"
+ @option_library = %q[-l"%s"]
+ @option_library_path = %q[-L"%s"]
+ @link_options = %Q[%{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
+ option_library_path % filename(f)
end
[flags, library_path_flags, _flags].flatten.join(' ')
end
@@ -168,24 +205,20 @@ module MRuby
[libraries, _libraries].flatten.map{ |d| option_library % d }.join(' ')
end
+ def run_attrs
+ [@libraries, @library_paths, @flags, @flags_before_libraries, @flags_after_libraries]
+ end
+
def run(outfile, objfiles, _libraries=[], _library_paths=[], _flags=[], _flags_before_libraries=[], _flags_after_libraries=[])
- FileUtils.mkdir_p File.dirname(outfile)
+ 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
+ _run link_options, { :flags => all_flags(_library_paths, _flags),
+ :outfile => filename(outfile) , :objs => filename(objfiles).map{|f| %Q["#{f}"]}.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
@@ -195,17 +228,13 @@ module MRuby
def initialize(build)
super
@command = ENV['AR'] || 'ar'
- @archive_options = 'rs %{outfile} %{objs}'
+ @archive_options = 'rs "%{outfile}" %{objs}'
end
def run(outfile, objfiles)
- FileUtils.mkdir_p File.dirname(outfile)
+ 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
+ _run archive_options, { :outfile => filename(outfile), :objs => filename(objfiles).map{|f| %Q["#{f}"]}.join(' ') }
end
end
@@ -215,11 +244,11 @@ module MRuby
def initialize(build)
super
@command = 'bison'
- @compile_options = '-o %{outfile} %{infile}'
+ @compile_options = %q[-o "%{outfile}" "%{infile}"]
end
def run(outfile, infile)
- FileUtils.mkdir_p File.dirname(outfile)
+ mkdir_p File.dirname(outfile)
_pp "YACC", infile.relative_path, outfile.relative_path
_run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) }
end
@@ -231,11 +260,11 @@ module MRuby
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}'
+ @compile_options = %q[-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)
+ mkdir_p File.dirname(outfile)
_pp "GPERF", infile.relative_path, outfile.relative_path
_run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) }
end
@@ -243,36 +272,50 @@ module MRuby
class Command::Git < Command
attr_accessor :flags
- attr_accessor :clone_options, :pull_options, :checkout_options
+ attr_accessor :clone_options, :pull_options, :checkout_options, :checkout_detach_options, :reset_options
def initialize(build)
super
@command = 'git'
- @flags = %w[]
+ @flags = []
@clone_options = "clone %{flags} %{url} %{dir}"
- @pull_options = "pull"
- @checkout_options = "checkout %{checksum_hash}"
+ @pull_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} pull"
+ @checkout_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} checkout %{checksum_hash}"
+ @checkout_detach_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} checkout --detach %{checksum_hash}"
+ @reset_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} reset %{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) }
+ _run clone_options, { :flags => [flags, _flags].flatten.join(' '), :url => shellquote(url), :dir => shellquote(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
+ _run pull_options, { :repo_dir => shellquote(dir) }
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
+ _pp "GIT CHECKOUT", dir, checksum_hash
+ _run checkout_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) }
+ end
+
+ def run_checkout_detach(dir, checksum_hash)
+ _pp "GIT CHECKOUT DETACH", dir, checksum_hash
+ _run checkout_detach_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) }
+ end
+
+ def run_reset_hard(dir, checksum_hash)
+ _pp "GIT RESET", dir, checksum_hash
+ _run reset_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) }
+ end
+
+ def commit_hash(dir)
+ `#{@command} --git-dir #{shellquote(dir +'/.git')} --work-tree #{shellquote(dir)} rev-parse --verify HEAD`.strip
+ end
+
+ def current_branch(dir)
+ `#{@command} --git-dir #{shellquote(dir + '/.git')} --work-tree #{shellquote(dir)} rev-parse --abbrev-ref HEAD`.strip
end
end
@@ -285,19 +328,24 @@ module MRuby
@compile_options = "-B%{funcname} -o-"
end
- def run(out, infiles, funcname)
+ def run(out, infiles, funcname, cdump: true, static: false)
@command ||= @build.mrbcfile
infiles = [infiles].flatten
- infiles.each do |f|
- _pp "MRBC", f.relative_path, nil, :indent => 2
+ infiles.each_with_index do |f, i|
+ _pp i == 0 ? "MRBC" : "", f.relative_path, indent: 2
end
- IO.popen("#{filename @command} #{@compile_options % {:funcname => funcname}} #{filename(infiles).join(' ')}", 'r+') do |io|
+ opt = @compile_options % {funcname: funcname}
+ opt << " -S" if cdump
+ opt << " -s" if static
+ cmd = %["#{filename @command}" #{opt} #{filename(infiles).map{|f| %["#{f}"]}.join(' ')}]
+ puts cmd if Rake.verbose
+ IO.popen(cmd, 'r+') do |io|
out.puts io.read
end
# if mrbc execution fail, drop the file
- if $?.exitstatus != 0
- File.delete(out.path)
- exit(-1)
+ unless $?.success?
+ rm_f out.path
+ fail "Command failed with status (#{$?.exitstatus}): [#{cmd[0,42]}...]"
end
end
end
diff --git a/lib/mruby/build/load_gems.rb b/lib/mruby/build/load_gems.rb
index 723be6ffc..6609a5c8e 100644
--- a/lib/mruby/build/load_gems.rb
+++ b/lib/mruby/build/load_gems.rb
@@ -13,13 +13,15 @@ module MRuby
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)
+ elsif GemBox.path
gemdir = File.expand_path(gemdir, File.dirname(GemBox.path))
else
+ caller_dir = File.expand_path(File.dirname(caller(1,1)[0][/^(.*?):\d/,1]))
+ if caller_dir == "#{MRUBY_ROOT}/build_config"
+ caller_dir = MRUBY_ROOT
+ end
gemdir = File.expand_path(gemdir, caller_dir)
end
@@ -29,18 +31,17 @@ module MRuby
Gem.current = nil
load gemrake
return nil unless Gem.current
+ current = 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
+ current.dir = gemdir
+ current.build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current
+ current.build_config_initializer = block
+ gems << current
- cxx_srcs = ['src', 'test', 'tools'].map do |subdir|
- Dir.glob("#{Gem.current.dir}/#{subdir}/*.{cpp,cxx,cc}")
- end.flatten
+ cxx_srcs = Dir.glob("#{current.dir}/{src,test,tools}/*.{cpp,cxx,cc}")
enable_cxx_exception unless cxx_srcs.empty?
- Gem.current
+ current
end
def load_special_path_gem(params)
@@ -58,7 +59,7 @@ module MRuby
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
+ mkdir_p mgem_list_dir
git.run_clone mgem_list_dir, mgem_list_url, "--depth 1"
end
@@ -83,27 +84,41 @@ module MRuby
# by default the 'master' branch is used
branch = params[:branch] ? params[:branch] : 'master'
+ lock = locks[url] if lock_enabled?
+
if File.exist?(gemdir)
if $pull_gems
+ # Jump to the top of the branch
+ git.run_checkout gemdir, branch
git.run_pull gemdir, url
- else
- gemdir
+ elsif params[:checksum_hash]
+ git.run_checkout_detach gemdir, params[:checksum_hash]
+ elsif lock
+ git.run_checkout_detach gemdir, lock['commit']
end
else
options = [params[:options]] || []
options << "--recursive"
options << "--branch \"#{branch}\""
- options << "--depth 1" unless params[:checksum_hash]
- FileUtils.mkdir_p "#{gem_clone_dir}"
+ options << "--depth 1" unless params[:checksum_hash] || lock
+ 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
+ if params[:checksum_hash]
+ git.run_checkout_detach gemdir, params[:checksum_hash]
+ elsif lock
+ git.run_checkout_detach gemdir, lock['commit']
+ end
+ end
+
+ if lock_enabled?
+ @gem_dir_to_repo_url[gemdir] = url unless params[:path]
+ locks[url] = {
+ 'url' => url,
+ 'branch' => git.current_branch(gemdir),
+ 'commit' => git.commit_hash(gemdir),
+ }
end
gemdir << "/#{params[:path]}" if params[:path]
diff --git a/lib/mruby/core_ext.rb b/lib/mruby/core_ext.rb
new file mode 100644
index 000000000..1ad528c26
--- /dev/null
+++ b/lib/mruby/core_ext.rb
@@ -0,0 +1,40 @@
+autoload :Pathname, 'pathname'
+
+class 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
+
+ def remove_leading_parents
+ Pathname.new(".#{Pathname.new("/#{self}").cleanpath}").cleanpath.to_s
+ end
+end
+
+def install_D(src, dst)
+ _pp "INSTALL", src.relative_path, dst.relative_path
+ rm_f dst
+ mkdir_p File.dirname(dst)
+ cp src, dst
+end
+
+def _pp(cmd, src, tgt=nil, indent: nil)
+ return if Rake.application.options.silent
+
+ width = 5
+ template = indent ? "%#{width * indent}s %s %s" : "%-#{width}s %s %s"
+ puts template % [cmd, src, tgt ? "-> #{tgt}" : nil]
+end
diff --git a/lib/mruby/gem.rb b/lib/mruby/gem.rb
index 7e97c34f3..fa0bb3a49 100644
--- a/lib/mruby/gem.rb
+++ b/lib/mruby/gem.rb
@@ -1,14 +1,12 @@
-require 'pathname'
require 'forwardable'
-require 'tsort'
-require 'shellwords'
+autoload :TSort, 'tsort'
+autoload :Shellwords, '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
@@ -18,7 +16,6 @@ module MRuby
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
@@ -28,8 +25,8 @@ module MRuby
alias :author= :authors=
attr_accessor :rbfiles, :objs
- attr_accessor :test_objs, :test_rbfiles, :test_args
- attr_accessor :test_preload
+ attr_writer :test_objs, :test_rbfiles
+ attr_accessor :test_args, :test_preload
attr_accessor :bins
@@ -46,35 +43,30 @@ module MRuby
@name = name
@initializer = block
@version = "0.0.0"
- @mrblib_dir = "mrblib"
- @objs_dir = "src"
+ @dependencies = []
+ @conflicts = []
MRuby::Gem.current = self
end
def setup
+ return if defined?(@bins) # return if already set up
+
MRuby::Gem.current = self
MRuby::Build::COMMANDS.each do |command|
instance_variable_set("@#{command}", @build.send(command).clone)
end
- @linker = LinkerConfig.new([], [], [], [], [])
+ @linker.run_attrs.each(&:clear)
- @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
+ @rbfiles = Dir.glob("#{@dir}/mrblib/**/*.rb").sort
+ @objs = srcs_to_objs("src")
- @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb")
- @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 = []
+ @cdump = true
@requirements = []
- @dependencies, @conflicts = [], []
@export_include_paths = []
@export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
@@ -87,21 +79,50 @@ module MRuby
fail "#{name || dir} required to set name, license(s) and author(s)"
end
- build.libmruby << @objs
+ build.libmruby_objs << @objs
instance_eval(&@build_config_initializer) if @build_config_initializer
+
+ repo_url = build.gem_dir_to_repo_url[dir]
+ build.locks[repo_url]['version'] = version if repo_url
end
def setup_compilers
- compilers.each do |compiler|
- compiler.define_rules build_dir, "#{dir}"
+ (core? ? [@cc, *(@cxx if build.cxx_exception_enabled?)] : compilers).each do |compiler|
+ compiler.define_rules build_dir, @dir, @build.exts.presym_preprocessed if build.presym_enabled?
+ compiler.define_rules build_dir, @dir, @build.exts.object
compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}]
- compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
+ compiler.include_paths << "#{@dir}/include" if File.directory? "#{@dir}/include"
end
define_gem_init_builder if @generate_functions
end
+ def for_windows?
+ if build.kind_of?(MRuby::CrossBuild)
+ return %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target)
+ elsif build.kind_of?(MRuby::Build)
+ return ('A'..'Z').to_a.any? { |vol| Dir.exist?("#{vol}:") }
+ end
+ return false
+ end
+
+ def disable_cdump
+ @cdump = false
+ end
+
+ def cdump?
+ build.presym_enabled? && @cdump
+ end
+
+ def core?
+ @dir.start_with?("#{MRUBY_ROOT}/mrbgems/")
+ end
+
+ def bin?
+ @bins.size > 0
+ end
+
def add_dependency(name, *requirements)
default_gem = requirements.last.kind_of?(Hash) ? requirements.pop : nil
requirements = ['>= 0.0.0'] if requirements.empty?
@@ -110,17 +131,13 @@ module MRuby
end
def add_test_dependency(*args)
- add_dependency(*args) if build.test_enabled?
+ add_dependency(*args) if build.test_enabled? || build.bintest_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
@@ -129,6 +146,14 @@ module MRuby
"#{build_dir}/gem_test.c"
end
+ def test_objs
+ @test_objs ||= srcs_to_objs("test")
+ end
+
+ def test_rbfiles
+ @test_rbfiles ||= Dir["#{@dir}/test/**/*.rb"].sort!
+ end
+
def search_package(name, version_query=nil)
package_query = name
package_query += " #{version_query}" if version_query
@@ -154,31 +179,53 @@ module MRuby
end
end
+ def srcs_to_objs(src_dir_from_gem_dir)
+ exts = compilers.flat_map{|c| c.source_exts} * ","
+ Dir["#{@dir}/#{src_dir_from_gem_dir}/*{#{exts}}"].map do |f|
+ objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
+ 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
+ mkdir_p build_dir
generate_gem_init("#{build_dir}/gem_init.c")
end
end
def generate_gem_init(fname)
+ _pp "GEN", fname.relative_path
open(fname, 'w') do |f|
print_gem_init_header f
- build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}" unless rbfiles.empty?
+ unless rbfiles.empty?
+ opts = {cdump: cdump?, static: true}
+ if cdump?
+ build.mrbc.run f, rbfiles, "gem_mrblib_#{funcname}_proc", **opts
+ else
+ build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}", **opts
+ end
+ end
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[ gem_mrblib_#{funcname}_proc_init_syms(mrb);] if !rbfiles.empty? && cdump?
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});]
+ if cdump?
+ f.puts %Q[ mrb_load_proc(mrb, gem_mrblib_#{funcname}_proc);]
+ else
+ f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});]
+ end
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[ }]
+ f.puts %Q[ struct REnv *e = mrb_vm_ci_env(mrb->c->cibase);]
+ f.puts %Q[ mrb_vm_ci_env_set(mrb->c->cibase, NULL);]
+ f.puts %Q[ mrb_env_unshare(mrb, e);]
end
f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
f.puts %Q[}]
@@ -202,9 +249,15 @@ module MRuby
def print_gem_init_header(f)
print_gem_comment(f)
- f.puts %Q[#include <stdlib.h>] unless rbfiles.empty?
- f.puts %Q[#include <mruby.h>]
- f.puts %Q[#include <mruby/irep.h>] unless rbfiles.empty?
+ if rbfiles.empty?
+ f.puts %Q[#include <mruby.h>]
+ else
+ f.puts %Q[#include <stdlib.h>]
+ unless cdump?
+ f.puts %Q[#include <mruby.h>]
+ f.puts %Q[#include <mruby/proc.h>]
+ end
+ end
end
def print_gem_test_header(f)
@@ -217,12 +270,8 @@ module MRuby
f.puts %Q[#include <mruby/hash.h>] unless test_args.empty?
end
- def test_dependencies
- [@name]
- end
-
def custom_test_init?
- @custom_test_init
+ !test_objs.empty?
end
def version_ok?(req_versions)
@@ -269,16 +318,18 @@ module MRuby
# ~> compare algorithm
#
# Example:
+ # ~> 2 means >= 2.0.0 and < 3.0.0
# ~> 2.2 means >= 2.2.0 and < 3.0.0
- # ~> 2.2.0 means >= 2.2.0 and < 2.3.0
+ # ~> 2.2.2 means >= 2.2.2 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
+ still_major_or_minor = (self <=> other.skip_major_or_minor) < 0
+ gr_or_eql and still_major_or_minor
end
- def skip_minor
+ def skip_major_or_minor
a = @ary.dup
+ a << 0 if a.size == 1 # ~> 2 can also be represented as ~> 2.0
a.slice!(-1)
a[-1] = a[-1].succ
a
@@ -312,6 +363,10 @@ module MRuby
@ary.each(&b)
end
+ def [](name)
+ @ary.detect {|g| g.name == name}
+ end
+
def <<(gem)
unless @ary.detect {|g| g.dir == gem.dir }
@ary << gem
@@ -334,26 +389,26 @@ module MRuby
end
def generate_gem_table build
- gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
+ gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec }
- default_gems = []
+ default_gems = {}
each do |g|
g.dependencies.each do |dep|
- default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
+ default_gems[dep[:gem]] ||= default_gem_params(dep)
end
end
until default_gems.empty?
- def_gem = default_gems.pop
+ def_name, def_gem = default_gems.shift
+ next if gem_table[def_name]
- spec = build.gem def_gem[:default]
- fail "Invalid gem name: #{spec.name} (Expected: #{def_gem[:gem]})" if spec.name != def_gem[:gem]
+ spec = gem_table[def_name] = build.gem(def_gem[:default])
+ fail "Invalid gem name: #{spec.name} (Expected: #{def_name})" if spec.name != def_name
spec.setup
spec.dependencies.each do |dep|
- default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
+ default_gems[dep[:gem]] ||= default_gem_params(dep)
end
- gem_table[spec.name] = spec
end
each do |g|
@@ -428,7 +483,8 @@ module MRuby
end
def import_include_paths(g)
- gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
+ gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec }
+
g.dependencies.each do |dep|
dep_g = gem_table[dep[:gem]]
# We can do recursive call safely
@@ -444,6 +500,12 @@ module MRuby
end
end
end
+
+ def linker_attrs(gem=nil)
+ gems = self.reject{|g| g.bin?} # library gems
+ gems << gem unless gem.nil?
+ gems.map{|g| g.linker.run_attrs}.transpose
+ end
end # List
end # Gem
diff --git a/lib/mruby/lockfile.rb b/lib/mruby/lockfile.rb
new file mode 100644
index 000000000..5abf804b4
--- /dev/null
+++ b/lib/mruby/lockfile.rb
@@ -0,0 +1,81 @@
+autoload :YAML, 'yaml'
+
+module MRuby
+ autoload :Source, 'mruby/source'
+
+ class Lockfile
+ class << self
+ def enable
+ @enabled = true
+ end
+
+ def disable
+ @enabled = false
+ end
+
+ def enabled?
+ @enabled
+ end
+
+ def build(target_name)
+ instance.build(target_name)
+ end
+
+ def write
+ instance.write if enabled?
+ end
+
+ def instance
+ @instance ||= new("#{MRUBY_CONFIG}.lock")
+ end
+ end
+
+ def initialize(filename)
+ @filename = filename
+ end
+
+ def build(target_name)
+ read[target_name] ||= {}
+ end
+
+ def write
+ locks = {"mruby" => mruby}
+ locks["builds"] = @builds if @builds
+ File.write(@filename, YAML.dump(locks))
+ end
+
+ private
+
+ def read
+ @builds ||= if File.exist?(@filename)
+ YAML.load_file(@filename)["builds"] || {}
+ else
+ {}
+ end
+ end
+
+ def shellquote(s)
+ if ENV['OS'] == 'Windows_NT'
+ "\"#{s}\""
+ else
+ "'#{s}'"
+ end
+ end
+
+ def mruby
+ mruby = {
+ 'version' => MRuby::Source::MRUBY_VERSION,
+ 'release_no' => MRuby::Source::MRUBY_RELEASE_NO,
+ }
+
+ git_dir = "#{MRUBY_ROOT}/.git"
+ if File.directory?(git_dir)
+ mruby['git_commit'] = `git --git-dir #{shellquote(git_dir)} --work-tree #{shellquote(MRUBY_ROOT)} rev-parse --verify HEAD`.strip
+ end
+
+ mruby
+ end
+
+ enable
+ end
+end
diff --git a/lib/mruby/presym.rb b/lib/mruby/presym.rb
new file mode 100644
index 000000000..016c2b20e
--- /dev/null
+++ b/lib/mruby/presym.rb
@@ -0,0 +1,130 @@
+module MRuby
+ class Presym
+ OPERATORS = {
+ "!" => "not",
+ "%" => "mod",
+ "&" => "and",
+ "*" => "mul",
+ "+" => "add",
+ "-" => "sub",
+ "/" => "div",
+ "<" => "lt",
+ ">" => "gt",
+ "^" => "xor",
+ "`" => "tick",
+ "|" => "or",
+ "~" => "neg",
+ "!=" => "neq",
+ "!~" => "nmatch",
+ "&&" => "andand",
+ "**" => "pow",
+ "+@" => "plus",
+ "-@" => "minus",
+ "<<" => "lshift",
+ "<=" => "le",
+ "==" => "eq",
+ "=~" => "match",
+ ">=" => "ge",
+ ">>" => "rshift",
+ "[]" => "aref",
+ "||" => "oror",
+ "<=>" => "cmp",
+ "===" => "eqq",
+ "[]=" => "aset",
+ }.freeze
+
+ SYMBOL_TO_MACRO = {
+ # Symbol => Macro
+ # [prefix, suffix] => [prefix, suffix]
+ ["@@" , "" ] => ["CV" , "" ],
+ ["@" , "" ] => ["IV" , "" ],
+ ["" , "!" ] => ["" , "_B" ],
+ ["" , "?" ] => ["" , "_Q" ],
+ ["" , "=" ] => ["" , "_E" ],
+ ["" , "" ] => ["" , "" ],
+ }.freeze
+
+ C_STR_LITERAL_RE = /"(?:[^\\\"]|\\.)*"/
+
+ def initialize(build)
+ @build = build
+ end
+
+ def scan(paths)
+ presym_hash = {}
+ paths.each {|path| read_preprocessed(presym_hash, path)}
+ presym_hash.keys.sort_by!{|sym| [c_literal_size(sym), sym]}
+ end
+
+ def read_list
+ File.readlines(list_path, mode: "r:binary").each(&:chomp!)
+ end
+
+ def write_list(presyms)
+ _pp "GEN", list_path.relative_path
+ File.binwrite(list_path, presyms.join("\n") << "\n")
+ end
+
+ def write_id_header(presyms)
+ prefix_re = Regexp.union(*SYMBOL_TO_MACRO.keys.map(&:first).uniq)
+ suffix_re = Regexp.union(*SYMBOL_TO_MACRO.keys.map(&:last).uniq)
+ sym_re = /\A(#{prefix_re})?([\w&&\D]\w*)(#{suffix_re})?\z/o
+ _pp "GEN", id_header_path.relative_path
+ File.open(id_header_path, "w:binary") do |f|
+ f.puts "enum mruby_presym {"
+ presyms.each.with_index(1) do |sym, num|
+ if sym_re =~ sym && (affixes = SYMBOL_TO_MACRO[[$1, $3]])
+ f.puts " MRB_#{affixes * 'SYM'}__#{$2} = #{num},"
+ elsif name = OPERATORS[sym]
+ f.puts " MRB_OPSYM__#{name} = #{num},"
+ end
+ end
+ f.puts "};"
+ f.puts
+ f.puts "#define MRB_PRESYM_MAX #{presyms.size}"
+ end
+ end
+
+ def write_table_header(presyms)
+ _pp "GEN", table_header_path.relative_path
+ File.open(table_header_path, "w:binary") do |f|
+ f.puts "static const uint16_t presym_length_table[] = {"
+ presyms.each{|sym| f.puts " #{sym.bytesize},\t/* #{sym} */"}
+ f.puts "};"
+ f.puts
+ f.puts "static const char * const presym_name_table[] = {"
+ presyms.each{|sym| f.puts %| "#{sym}",|}
+ f.puts "};"
+ end
+ end
+
+ def list_path
+ @list_path ||= "#{@build.build_dir}/presym".freeze
+ end
+
+ def header_dir;
+ @header_dir ||= "#{@build.build_dir}/include/mruby/presym".freeze
+ end
+
+ def id_header_path
+ @id_header_path ||= "#{header_dir}/id.h".freeze
+ end
+
+ def table_header_path
+ @table_header_path ||= "#{header_dir}/table.h".freeze
+ end
+
+ private
+
+ def read_preprocessed(presym_hash, path)
+ File.binread(path).scan(/<@! (.*?) !@>/) do |part,|
+ literals = part.scan(C_STR_LITERAL_RE)
+ presym_hash[literals.map{|l| l[1..-2]}.join] = true unless literals.empty?
+ end
+ end
+
+ def c_literal_size(literal_without_quote)
+ literal_without_quote.size # TODO: consider escape sequence
+ end
+ end
+end
diff --git a/lib/mruby/source.rb b/lib/mruby/source.rb
index 5819a322b..a305c7b04 100644
--- a/lib/mruby/source.rb
+++ b/lib/mruby/source.rb
@@ -6,7 +6,9 @@ module MRuby
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_READ_VERSION_CONSTANT = Proc.new do |name|
+ ROOT.join('include','mruby','version.h').read.match(/^#define #{name} +"?([\w\. ]+)"?\r?$/)[1]
+ end
MRUBY_RUBY_VERSION = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_VERSION']
MRUBY_RUBY_ENGINE = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_ENGINE']
diff --git a/minirake b/minirake
index 86c42c999..643e5baec 100755
--- a/minirake
+++ b/minirake
@@ -1,605 +1,2 @@
#!/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
+exec "rake", *ARGV
diff --git a/mrbgems/default-no-fpu.gembox b/mrbgems/default-no-fpu.gembox
new file mode 100644
index 000000000..d671abd5c
--- /dev/null
+++ b/mrbgems/default-no-fpu.gembox
@@ -0,0 +1,3 @@
+MRuby::GemBox.new do |conf|
+ conf.gembox "stdlib"
+end
diff --git a/mrbgems/default-no-stdio.gembox b/mrbgems/default-no-stdio.gembox
new file mode 100644
index 000000000..064a57f99
--- /dev/null
+++ b/mrbgems/default-no-stdio.gembox
@@ -0,0 +1,4 @@
+MRuby::GemBox.new do |conf|
+ conf.gembox "stdlib"
+ conf.gembox "math"
+end
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index 23e65fcee..ae2de2ac2 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -1,75 +1,12 @@
MRuby::GemBox.new do |conf|
- # Meta-programming features
- conf.gem :core => "mruby-metaprog"
+ conf.gembox "stdlib"
+ conf.gembox "stdlib-ext"
+ conf.gembox "stdlib-io"
+ conf.gembox "math"
+ conf.gembox "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 mrbc command
+ conf.gem :core => "mruby-bin-mrbc"
# Generate mirb command
conf.gem :core => "mruby-bin-mirb"
@@ -80,12 +17,6 @@ MRuby::GemBox.new do |conf|
# 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"
+ # Generate mruby-config command
+ conf.gem :core => "mruby-bin-config"
end
diff --git a/mrbgems/full-core.gembox b/mrbgems/full-core.gembox
index 9a5b7081b..e0d008f06 100644
--- a/mrbgems/full-core.gembox
+++ b/mrbgems/full-core.gembox
@@ -1,9 +1,6 @@
MRuby::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)$/
+ conf.gem :core => g unless g =~ /^mruby-(?:bin-debugger|test)$/
end
end
diff --git a/mrbgems/math.gembox b/mrbgems/math.gembox
new file mode 100644
index 000000000..88f70ddde
--- /dev/null
+++ b/mrbgems/math.gembox
@@ -0,0 +1,10 @@
+# It also works with MRB_NO_STDIO.
+
+MRuby::GemBox.new do |conf|
+ # Use standard Math module
+ conf.gem :core => "mruby-math"
+
+ # Use Rational/Complex numbers
+ conf.gem :core => "mruby-rational"
+ conf.gem :core => "mruby-complex"
+end
diff --git a/mrbgems/metaprog.gembox b/mrbgems/metaprog.gembox
new file mode 100644
index 000000000..db35d307c
--- /dev/null
+++ b/mrbgems/metaprog.gembox
@@ -0,0 +1,15 @@
+# It also works with MRB_NO_STDIO and MRB_NO_FLOAT.
+
+MRuby::GemBox.new do |conf|
+ # Meta-programming features
+ conf.gem :core => "mruby-metaprog"
+
+ # Use Method/UnboundMethod class
+ conf.gem :core => "mruby-method"
+
+ # Use eval()
+ conf.gem :core => "mruby-eval"
+
+ # Use mruby-compiler to build other mrbgems
+ conf.gem :core => "mruby-compiler"
+end
diff --git a/mrbgems/mruby-array-ext/mrbgem.rake b/mrbgems/mruby-array-ext/mrbgem.rake
index 58d4428d4..882caf1ab 100644
--- a/mrbgems/mruby-array-ext/mrbgem.rake
+++ b/mrbgems/mruby-array-ext/mrbgem.rake
@@ -2,5 +2,4 @@ MRuby::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
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index ed3f591fe..7520b932f 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -1,32 +1,6 @@
-# coding: cp932
class Array
##
# call-seq:
- # Array.try_convert(obj) -> array or nil
- #
- # Tries to convert +obj+ into an array, using +to_ary+ method.
- # converted array or +nil+ if +obj+ cannot be converted for any reason.
- # This method can be used to check if an argument is an array.
- #
- # Array.try_convert([1]) #=> [1]
- # Array.try_convert("1") #=> nil
- #
- # if tmp = Array.try_convert(arg)
- # # the argument is an array
- # elsif tmp = String.try_convert(arg)
- # # the argument is a string
- # end
- #
- def self.try_convert(obj)
- if obj.respond_to?(:to_ary)
- obj.to_ary
- else
- nil
- end
- end
-
- ##
- # call-seq:
# ary.uniq! -> ary or nil
# ary.uniq! { |item| ... } -> ary or nil
#
@@ -77,7 +51,7 @@ class Array
# b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
#
def uniq(&block)
- ary = self.dup
+ ary = self[0..-1]
ary.uniq!(&block)
ary
end
@@ -116,6 +90,22 @@ class Array
##
# call-seq:
+ # ary.difference(other_ary1, other_ary2, ...) -> new_ary
+ #
+ # Returns a new array that is a copy of the original array, removing all
+ # occurrences of any item that also appear in +other_ary+. The order is
+ # preserved from the original array.
+ #
+ def difference(*args)
+ ary = self
+ args.each do |x|
+ ary = ary - x
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
# ary | other_ary -> new_ary
#
# Set Union---Returns a new array by joining this array with
@@ -160,7 +150,7 @@ class Array
# [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
#
def &(elem)
- raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
+ raise TypeError, "cannot convert #{elem.class} into Array" unless elem.class == Array
hash = {}
array = []
@@ -185,6 +175,65 @@ class Array
##
# call-seq:
+ # ary.intersection(other_ary,...) -> new_ary
+ #
+ # Set Intersection---Returns a new array containing elements common to
+ # this array and <i>other_ary</i>s, removing duplicates. The order is
+ # preserved from the original array.
+ #
+ # [1, 2, 3].intersection([3, 4, 1], [1, 3, 5]) #=> [1, 3]
+ #
+ def intersection(*args)
+ ary = self
+ args.each do |x|
+ ary = ary & x
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
+ # ary.intersect?(other_ary) -> true or false
+ #
+ # Returns +true+ if the array and +other_ary+ have at least one element in
+ # common, otherwise returns +false+.
+ #
+ # a = [ 1, 2, 3 ]
+ # b = [ 3, 4, 5 ]
+ # c = [ 5, 6, 7 ]
+ # a.intersect?(b) #=> true
+ # a.intersect?(c) #=> false
+ def intersect?(ary)
+ raise TypeError, "cannot convert #{ary.class} into Array" unless ary.class == Array
+
+ hash = {}
+ if self.length > ary.length
+ shorter = ary
+ longer = self
+ else
+ shorter = self
+ longer = ary
+ end
+ idx = 0
+ len = shorter.size
+ while idx < len
+ hash[shorter[idx]] = true
+ idx += 1
+ end
+ idx = 0
+ len = size
+ while idx < len
+ v = longer[idx]
+ if hash[v]
+ return true
+ end
+ idx += 1
+ end
+ false
+ end
+
+ ##
+ # call-seq:
# ary.flatten -> new_ary
# ary.flatten(level) -> new_ary
#
@@ -201,7 +250,7 @@ class Array
# a.flatten(1) #=> [1, 2, 3, [4, 5]]
#
def flatten(depth=nil)
- res = dup
+ res = Array.new(self)
res.flatten! depth
res
end
@@ -245,41 +294,6 @@ class Array
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
- # <i>ary</i>.
- #
- # [ "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
@@ -292,7 +306,6 @@ class Array
self
end
- NONE=Object.new
##
# call-seq:
# ary.fetch(index) -> obj
@@ -317,8 +330,8 @@ class Array
# #=> "100 is out of bounds"
#
- def fetch(n=nil, ifnone=NONE, &block)
- warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
+ def fetch(n, ifnone=NONE, &block)
+ #warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
idx = n
if idx < 0
@@ -373,7 +386,6 @@ class Array
end
beg = len = 0
- ary = []
if block
if arg0.nil? && arg1.nil? && arg2.nil?
# ary.fill { |index| block } -> ary
@@ -437,57 +449,6 @@ class Array
##
# 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
#
@@ -681,37 +642,6 @@ class Array
##
# 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
#
@@ -729,7 +659,6 @@ class Array
return to_enum :keep_if unless block
idx = 0
- len = self.size
while idx < self.size do
if block.call(self[idx])
idx += 1
@@ -799,16 +728,6 @@ class Array
end
##
- # call-seq:
- # ary.to_ary -> ary
- #
- # Returns +self+.
- #
- def to_ary
- self
- end
-
- ##
# call-seq:
# ary.dig(idx, ...) -> object
#
@@ -852,12 +771,11 @@ class Array
# 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
+ size = self.size
if n == 0
- yield []
- else
+ yield []
+ elsif 0 < n && n <= size
i = 0
while i<size
result = [self[i]]
@@ -872,6 +790,7 @@ class Array
i += 1
end
end
+ self
end
##
@@ -898,9 +817,8 @@ class Array
# 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
+ size = self.size
if n == 0
yield []
elsif n == 1
@@ -909,7 +827,7 @@ class Array
yield [self[i]]
i += 1
end
- else
+ elsif n <= size
i = 0
while i<size
result = [self[i]]
@@ -919,6 +837,7 @@ class Array
i += 1
end
end
+ self
end
##
@@ -927,7 +846,7 @@ class Array
#
# 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.
+ # If the length of the subarrays don't match, an IndexError is raised.
#
# Examples:
#
@@ -940,8 +859,8 @@ class Array
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
+ column_count ||= row.size
+ raise IndexError, 'element size differs' unless column_count == row.size
end
Array.new(column_count) do |column_index|
@@ -954,7 +873,7 @@ class Array
# ary.to_h -> Hash
# ary.to_h{|item| ... } -> Hash
#
- # Returns the result of interpreting <i>aray</i> as an array of
+ # Returns the result of interpreting <i>array</i> as an array of
# <tt>[key, value]</tt> pairs. If a block is given, it should
# return <tt>[key, value]</tt> pairs to construct a hash.
#
@@ -973,4 +892,8 @@ class Array
end
h
end
+
+ alias append push
+ alias prepend unshift
+ alias filter! select!
end
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index b6d9c9c80..d97778642 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -3,6 +3,7 @@
#include <mruby/array.h>
#include <mruby/range.h>
#include <mruby/hash.h>
+#include <mruby/presym.h>
/*
* call-seq:
@@ -28,9 +29,8 @@ 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);
+ mrb_value v;
+ mrb_value k = mrb_get_arg1(mrb);
for (i = 0; i < RARRAY_LEN(ary); ++i) {
v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]);
@@ -59,13 +59,12 @@ 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);
+ mrb_value v;
+ mrb_value value = mrb_get_arg1(mrb);
for (i = 0; i < RARRAY_LEN(ary); ++i) {
v = RARRAY_PTR(ary)[i];
- if (mrb_type(v) == MRB_TT_ARRAY &&
+ if (mrb_array_p(v) &&
RARRAY_LEN(v) > 1 &&
mrb_equal(mrb, RARRAY_PTR(v)[1], value))
return v;
@@ -96,17 +95,22 @@ mrb_ary_at(mrb_state *mrb, mrb_value ary)
}
static mrb_value
+ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n)
+{
+ return mrb_ary_entry(ary, n);
+}
+
+static mrb_value
mrb_ary_values_at(mrb_state *mrb, mrb_value self)
{
mrb_int argc;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_get_args(mrb, "*", &argv, &argc);
- return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref);
+ return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, ary_ref);
}
-
/*
* call-seq:
* ary.slice!(index) -> obj or nil
@@ -140,22 +144,21 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
mrb_ary_modify(mrb, a);
if (mrb_get_argc(mrb) == 1) {
- mrb_value index;
+ mrb_value index = mrb_get_arg1(mrb);
- 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) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
goto delete_pos_len;
}
else {
return mrb_nil_value();
}
- case MRB_TT_FIXNUM:
- val = mrb_funcall(mrb, self, "delete_at", 1, index);
+ case MRB_TT_INTEGER:
+ val = mrb_funcall_id(mrb, self, MRB_SYM(delete_at), 1, index);
return val;
default:
- val = mrb_funcall(mrb, self, "delete_at", 1, index);
+ val = mrb_funcall_id(mrb, self, MRB_SYM(delete_at), 1, index);
return val;
}
}
@@ -185,6 +188,168 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
return ary;
}
+/*
+ * 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" ]
+ */
+
+static mrb_value
+mrb_ary_compact(mrb_state *mrb, mrb_value self)
+{
+ mrb_value ary = mrb_ary_new(mrb);
+ mrb_int len = RARRAY_LEN(self);
+ mrb_value *p = RARRAY_PTR(self);
+
+ for (mrb_int i = 0; i < len; ++i) {
+ if (!mrb_nil_p(p[i])) {
+ mrb_ary_push(mrb, ary, p[i]);
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * ary.compact! -> ary or nil
+ *
+ * Removes +nil+ elements from the array.
+ * Returns +nil+ if no changes were made, otherwise returns
+ * <i>ary</i>.
+ *
+ * [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ]
+ * [ "a", "b", "c" ].compact! #=> nil
+ */
+static mrb_value
+mrb_ary_compact_bang(mrb_state *mrb, mrb_value self)
+{
+ struct RArray *a = mrb_ary_ptr(self);
+ mrb_int i, j = 0;
+ mrb_int len = ARY_LEN(a);
+ mrb_value *p = ARY_PTR(a);
+
+ mrb_ary_modify(mrb, a);
+ for (i = 0; i < len; ++i) {
+ if (!mrb_nil_p(p[i])) {
+ if (i != j) p[j] = p[i];
+ j++;
+ }
+ }
+ if (i == j) return mrb_nil_value();
+ if (j < len) ARY_SET_LEN(RARRAY(self), j);
+ return self;
+}
+
+
+/*
+ * 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"]
+ */
+static mrb_value
+mrb_ary_rotate(mrb_state *mrb, mrb_value self)
+{
+ mrb_value ary = mrb_ary_new(mrb);
+ mrb_int len = RARRAY_LEN(self);
+ mrb_value *p = RARRAY_PTR(self);
+ mrb_int count=1, idx;
+
+ mrb_get_args(mrb, "|i", &count);
+ if (len <= 0) return ary;
+ if (count < 0) {
+ idx = len - (~count % len) - 1;
+ }
+ else {
+ idx = count % len;
+ }
+ for (mrb_int i = 0; i<len; i++) {
+ mrb_ary_push(mrb, ary, p[idx++]);
+ if (idx == len) idx = 0;
+ }
+ return ary;
+}
+
+static void
+rev(mrb_value *p, mrb_int beg, mrb_int end)
+{
+ for (mrb_int i=beg,j=end-1; i<j; i++,j--) {
+ mrb_value v = p[i];
+ p[i] = p[j];
+ p[j] = v;
+ }
+}
+
+/*
+ * 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"]
+ */
+static mrb_value
+mrb_ary_rotate_bang(mrb_state *mrb, mrb_value self)
+{
+ struct RArray *a = mrb_ary_ptr(self);
+ mrb_int len = ARY_LEN(a);
+ mrb_value *p = ARY_PTR(a);
+ mrb_int count=1, idx;
+
+ mrb_get_args(mrb, "|i", &count);
+ mrb_ary_modify(mrb, a);
+ if (len == 0 || count == 0) return self;
+ if (count == 1) {
+ mrb_value v = p[0];
+ for (mrb_int i=1; i<len; i++) {
+ p[i-1] = p[i];
+ }
+ p[len-1] = v;
+ return self;
+ }
+ if (count < 0) {
+ idx = len - (~count % len) - 1;
+ }
+ else {
+ idx = count % len;
+ }
+ /* e.g. [1,2,3,4,5].rotate!(2) -> [3,4,5,1,2] */
+ /* first, reverse the whole array */
+ /* [1,2,3,4,5] -> [5,4,3,2,1] */
+ rev(p, 0, len);
+ /* then, re-reverse part before idx */
+ /* [5,4,3,2,1] -> [3,4,5,2,1] */
+ /* ^idx ~~~~~ */
+ rev(p, 0, len-idx);
+ /* finally, re-reverse part after idx */
+ /* [3,4,5,2,1] -> [3,4,5,1,2] */
+ /* ^idx ~~~ */
+ rev(p, len-idx, len);
+ return self;
+}
+
void
mrb_mruby_array_ext_gem_init(mrb_state* mrb)
{
@@ -194,7 +359,11 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
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());
+ mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ARG(1,1));
+ mrb_define_method(mrb, a, "compact", mrb_ary_compact, MRB_ARGS_NONE());
+ mrb_define_method(mrb, a, "compact!", mrb_ary_compact_bang, MRB_ARGS_NONE());
+ mrb_define_method(mrb, a, "rotate", mrb_ary_rotate, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, a, "rotate!", mrb_ary_rotate_bang, MRB_ARGS_OPT(1));
}
void
diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb
index 84f9cfeaf..3f73ad8b9 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -1,11 +1,21 @@
##
# Array(Ext) Test
-assert("Array.try_convert") do
- assert_nil Array.try_convert(0)
- assert_nil Array.try_convert(nil)
- assert_equal [], Array.try_convert([])
- assert_equal [1,2,3], Array.try_convert([1,2,3])
+def assert_permutation_combination(exp, receiver, meth, *args)
+ act = []
+ ret = receiver.__send__(meth, *args) { |v| act << v }
+ assert "assert_#{meth}" do
+ assert_equal(exp, act.sort)
+ assert_same(receiver, ret)
+ end
+end
+
+def assert_permutation(exp, receiver, *args)
+ assert_permutation_combination(exp, receiver, :permutation, *args)
+end
+
+def assert_combination(exp, receiver, *args)
+ assert_permutation_combination(exp, receiver, :combination, *args)
end
assert("Array#assoc") do
@@ -83,6 +93,14 @@ assert("Array#union") do
assert_equal [1, 2, 3, 4, 5], a.union(b,c)
end
+assert("Array#difference") do
+ a = [1, 2, 3, 1, 6, 7]
+ b = [1, 4, 6]
+ c = [1, 5, 7]
+
+ assert_equal [2, 3], a.difference(b,c)
+end
+
assert("Array#&") do
a = [1, 2, 3, 1]
b = [1, 4]
@@ -93,6 +111,22 @@ assert("Array#&") do
assert_equal [1, 2, 3, 1], a
end
+assert("Array#intersection") do
+ a = [1, 2, 3, 1, 8, 6, 7, 8]
+ b = [1, 4, 6, 8]
+ c = [1, 5, 7, 8]
+
+ assert_equal [1, 8], a.intersection(b,c)
+end
+
+assert("Array#intersect?") do
+ a = [ 1, 2, 3 ]
+ b = [ 3, 4, 5 ]
+ c = [ 5, 6, 7 ]
+ assert_true(a.intersect?(b))
+ assert_false(a.intersect?(c))
+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
@@ -169,12 +203,6 @@ assert("Array#reverse_each") do
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
@@ -275,22 +303,9 @@ assert("Array#bsearch") do
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
+# tested through Array#bsearch
+#assert("Array#bsearch_index") do
+#end
assert("Array#keep_if") do
a = [1, 2, 3, 4, 5]
@@ -338,27 +353,11 @@ assert('Array#to_h') do
assert_raise(ArgumentError) { [[1]].to_h }
end
-assert('Array#to_h (Modified)') do
- class A
- def to_ary
- $a.clear
- nil
- end
- end
- $a = [A.new]
- assert_raise(TypeError) { $a.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#to_ary") do
- assert_equal [], [].to_ary
- assert_equal [1,2,3], [1,2,3].to_ary
-end
-
assert("Array#dig") do
h = [[[1]], 0]
assert_equal(1, h.dig(0, 0, 0))
@@ -392,30 +391,24 @@ 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)
+ assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a)
+ assert_permutation([[1],[2],[3]], a, 1)
+ assert_permutation([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]], a, 2)
+ assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a, 3)
+ assert_permutation([[]], a, 0)
+ assert_permutation([], a, 4)
+ assert_permutation([], a, -1)
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)
+ assert_combination([[1],[2],[3],[4]], a, 1)
+ assert_combination([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a, 2)
+ assert_combination([[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a, 3)
+ assert_combination([[1,2,3,4]], a, 4)
+ assert_combination([[]], a, 0)
+ assert_combination([], a, 5)
+ assert_combination([], a, -1)
end
assert('Array#transpose') do
diff --git a/mrbgems/mruby-bin-config/mrbgem.rake b/mrbgems/mruby-bin-config/mrbgem.rake
new file mode 100644
index 000000000..b4c437640
--- /dev/null
+++ b/mrbgems/mruby-bin-config/mrbgem.rake
@@ -0,0 +1,35 @@
+iscross = MRuby::Build.current.kind_of?(MRuby::CrossBuild)
+
+MRuby::Gem::Specification.new('mruby-bin-config') do |spec|
+ name = 'mruby-config'
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = "#{name} command"
+
+ if iscross
+ mruby_config_dir = "#{build.build_dir}/host-bin"
+ else
+ mruby_config_dir = "#{build.build_dir}/bin"
+ end
+ mruby_config = name + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
+ mruby_config_path = "#{mruby_config_dir}/#{mruby_config}"
+ make_cfg = "#{build.build_dir}/lib/libmruby.flags.mak"
+ tmplt_path = "#{__dir__}/#{mruby_config}"
+
+ if iscross
+ build.products << mruby_config_path
+ else
+ build.bins << mruby_config
+ end
+
+ directory mruby_config_dir
+
+ file mruby_config_path => [mruby_config_dir, make_cfg, tmplt_path] do |t|
+ config = Hash[File.readlines(make_cfg).map!(&:chomp).map! {|l|
+ l.gsub('\\"', '"').split(' = ', 2).map! {|s| s.sub(/^(?=.)/, 'echo ')}
+ }]
+ tmplt = File.read(tmplt_path)
+ File.write(t.name, tmplt.gsub(/(#{Regexp.union(*config.keys)})\b/, config))
+ chmod(0755, t.name)
+ end
+end
diff --git a/mrbgems/mruby-bin-config/mruby-config b/mrbgems/mruby-bin-config/mruby-config
new file mode 100644
index 000000000..3adda9e1a
--- /dev/null
+++ b/mrbgems/mruby-bin-config/mruby-config
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+print_help()
+{
+ echo "Usage: mruby-config [switches]"
+ echo " switches:"
+ echo " --cc print compiler name"
+ echo " --cflags print flags passed to compiler"
+ echo " --ld print linker name"
+ 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"
+ echo " --help print this help"
+ exit 0
+}
+
+if [ $# -eq 0 ]; then
+ print_help
+fi
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ --cc) echo MRUBY_CC;;
+ --cflags) echo MRUBY_CFLAGS;;
+ --ld) echo MRUBY_LD;;
+ --ldflags) echo MRUBY_LDFLAGS;;
+ --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;;
+ --libs) echo MRUBY_LIBS;;
+ --libmruby-path) echo MRUBY_LIBMRUBY_PATH;;
+ *) print_help;;
+ esac
+ shift
+done
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config.bat b/mrbgems/mruby-bin-config/mruby-config.bat
index a1f7bfdd1..949fea06f 100644
--- a/mrbgems/mruby-bin-mruby-config/mruby-config.bat
+++ b/mrbgems/mruby-bin-config/mruby-config.bat
@@ -3,7 +3,9 @@
:top
shift
if "%0" equ "" goto :eof
+if "%0" equ "--cc" goto cc
if "%0" equ "--cflags" goto cflags
+if "%0" equ "--ld" goto ld
if "%0" equ "--ldflags" goto ldflags
if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs
if "%0" equ "--libs" goto libs
@@ -12,10 +14,18 @@ if "%0" equ "--help" goto showhelp
echo Invalid Option
goto :eof
+:cc
+echo MRUBY_CC
+goto top
+
:cflags
echo MRUBY_CFLAGS
goto top
+:ld
+echo MRUBY_LD
+goto top
+
:libs
echo MRUBY_LIBS
goto top
@@ -35,7 +45,9 @@ goto top
:showhelp
echo Usage: mruby-config [switches]
echo switches:
+echo --cc print compiler name
echo --cflags print flags passed to compiler
+echo --ld print linker name
echo --ldflags print flags passed to linker
echo --ldflags-before-libs print flags passed to linker before linked libraries
echo --libs print linked libraries
diff --git a/mrbgems/mruby-bin-debugger/bintest/mrdb.rb b/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
index 26f3138aa..bc5dc4552 100644
--- a/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
+++ b/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
@@ -13,14 +13,14 @@ class BinTest_MrubyBinDebugger
script.flush
# compile
- `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
+ `#{cmd("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|
+ ["#{cmd('mrdb')} #{script.path}", "#{cmd('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)}
@@ -63,10 +63,7 @@ assert('mruby-bin-debugger(mrdb) command line') do
# ruby source
src = "foo = 'foo'\n"
- str = ""
- 103.times {
- str += "1234567890"
- }
+ str = ":#{'abcdefghij' * 103}"
cmd = "p a=#{str}"
# test case
diff --git a/mrbgems/mruby-bin-debugger/bintest/print.rb b/mrbgems/mruby-bin-debugger/bintest/print.rb
index 6675392b8..63ebded3e 100644
--- a/mrbgems/mruby-bin-debugger/bintest/print.rb
+++ b/mrbgems/mruby-bin-debugger/bintest/print.rb
@@ -1,9 +1,10 @@
require 'open3'
require 'tempfile'
+require 'strscan'
class BinTest_MrubyBinDebugger
- @debug1=false
- @debug2=true
+# @debug1=false
+# @debug2=true
def self.test(rubysource, testcase)
script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
@@ -12,17 +13,27 @@ class BinTest_MrubyBinDebugger
script.flush
# compile
- `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
+ `#{cmd("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|
+ prompt = /^\(#{Regexp.escape(script.path)}:\d+\) /
+ ["#{cmd('mrdb')} #{script.path}", "#{cmd('mrdb')} -b #{bin.path}"].each do |cmd|
o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
+ scanner = StringScanner.new(o)
+ scanner.skip_until(prompt)
+ testcase.each do |tc|
+ exp = tc[:exp]
+ if exp
+ act = scanner.scan_until(/\n/)
+ break unless assert_operator act, :start_with?, exp
+ end
+ scanner.skip_until(prompt)
+ end
- exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
=begin
if @debug1
o.split("\n").each_with_index do |i,actual|
@@ -41,14 +52,6 @@ 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
@@ -64,7 +67,7 @@ assert('mruby-bin-debugger(print) invalid arguments') do
BinTest_MrubyBinDebugger.test(src, tc)
end
-assert('mruby-bin-debugger(print) nomal') do
+assert('mruby-bin-debugger(print) normal') do
# ruby source
src = <<"SRC"
foo = 'foo'
@@ -90,13 +93,13 @@ assert('mruby-bin-debugger(print) error') do
# test case
tc = []
- tc << {:cmd=>"p (1+2", :exp=>'$1 = SyntaxError'}
- tc << {:cmd=>"p bar", :exp=>'$2 = (eval):2: undefined method'}
+ tc << {:cmd=>"p (1+2", :exp=>'$1 = line 1: syntax error'}
+ tc << {:cmd=>"p bar", :exp=>'$2 = undefined method'}
BinTest_MrubyBinDebugger.test(src, tc)
end
-# Kernel#instance_eval(string) does't work multiple statements.
+# Kernel#instance_eval(string) doesn't work multiple statements.
=begin
assert('mruby-bin-debugger(print) multiple statements') do
# ruby source
@@ -231,7 +234,7 @@ SRC
BinTest_MrubyBinDebugger.test(src, tc)
end
-assert('mruby-bin-debugger(print) same name:local variabe') do
+assert('mruby-bin-debugger(print) same name:local variable') do
# ruby source (bp is break point)
src = <<"SRC"
lv = 'top'
@@ -261,7 +264,7 @@ SRC
BinTest_MrubyBinDebugger.test(src, tc)
end
-assert('mruby-bin-debugger(print) same name:instance variabe') do
+assert('mruby-bin-debugger(print) same name:instance variable') do
# ruby source (bp is break point)
src = <<"SRC"
@iv = 'top'
@@ -293,7 +296,7 @@ SRC
BinTest_MrubyBinDebugger.test(src, tc)
end
-# Kernel#instance_eval(string) does't work const.
+# Kernel#instance_eval(string) doesn't work const.
=begin
assert('mruby-bin-debugger(print) same name:const') do
# ruby source (bp is break point)
@@ -513,7 +516,7 @@ SRC
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 a/b', :exp=>'$4 = 0'}
tc << {:cmd=>'p c%b', :exp=>'$5 = 3'}
tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'}
tc << {:cmd=>'p ~3', :exp=>'$7 = -4'}
@@ -588,7 +591,7 @@ SRC
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'}
+ tc << {:cmd=>'p "#{undefined}"', :exp=>'$4 = undefined method'}
BinTest_MrubyBinDebugger.test(src, tc)
end
@@ -611,13 +614,13 @@ SRC
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 a/=4', :exp=>'$4 = 2'}
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 "#{a} #{b} #{c}"', :exp=>'$8 = "2 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]'}
@@ -626,7 +629,7 @@ SRC
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'}
+ tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = undefined method'}
BinTest_MrubyBinDebugger.test(src, tc)
end
@@ -679,13 +682,13 @@ SRC
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 a/=4', :exp=>'$4 = 2'}
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 "#{a} #{b} #{c}"', :exp=>'$8 = "2 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]'}
@@ -694,8 +697,7 @@ SRC
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'}
+ tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = undefined method'}
BinTest_MrubyBinDebugger.test(src, tc)
end
-
diff --git a/mrbgems/mruby-bin-debugger/mrbgem.rake b/mrbgems/mruby-bin-debugger/mrbgem.rake
index 764f431af..091851dd4 100644
--- a/mrbgems/mruby-bin-debugger/mrbgem.rake
+++ b/mrbgems/mruby-bin-debugger/mrbgem.rake
@@ -2,7 +2,7 @@ MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'mruby debugger command'
-
+ spec.build.defines |= %w(MRB_USE_DEBUG_HOOK)
spec.add_dependency('mruby-eval', :core => 'mruby-eval')
spec.bins = %w(mrdb)
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
index d3ccf08ae..a26630599 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
@@ -14,30 +14,62 @@
#include <mruby/variable.h>
#include "mrdberror.h"
#include "apibreak.h"
+#include "apistring.h"
#define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024)
#define MRB_DEBUG_BP_FILE_OK (0x0001)
#define MRB_DEBUG_BP_LINENO_OK (0x0002)
+static uint32_t
+packed_int_decode(uint8_t *p, uint8_t **newpos)
+{
+ size_t i = 0, shift = 0;
+ uint32_t n = 0;
+
+ do {
+ n |= ((uint32_t)(p[i] & 0x7f)) << shift;
+ i++;
+ shift += 7;
+ } while (shift < sizeof(uint32_t) * 8 && (p[i - 1] & 0x80));
+ if (newpos) *newpos = p + i;
+ return n;
+}
+
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) {
+ switch (info_file->line_type) {
+ case mrb_debug_line_ary:
for (l_idx = 0; l_idx < count; ++l_idx) {
if (lineno == info_file->lines.ary[l_idx]) {
return lineno;
}
}
- }
- else {
+ break;
+
+ case mrb_debug_line_flat_map:
for (l_idx = 0; l_idx < count; ++l_idx) {
if (lineno == info_file->lines.flat_map[l_idx].line) {
return lineno;
}
}
+ break;
+
+ case mrb_debug_line_packed_map:
+ {
+ uint8_t *p = info_file->lines.packed_map;
+ uint8_t *pend = p + count;
+ uint32_t line = 0;
+ while (p < pend) {
+ packed_int_decode(p, &p);
+ line += packed_int_decode(p, &p);
+ if (line == lineno) return lineno;
+ }
+ }
+ break;
}
return 0;
@@ -84,7 +116,7 @@ free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
}
static uint16_t
-check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
+check_file_lineno(mrb_state *mrb, const struct mrb_irep *irep, const char *file, uint16_t lineno)
{
mrb_irep_debug_info_file *info_file;
uint16_t result = 0;
@@ -93,8 +125,10 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
uint16_t i;
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
+ const char *filename;
info_file = irep->debug_info->files[f_idx];
- if (!strcmp(info_file->filename, file)) {
+ filename = mrb_sym_name_len(mrb, info_file->filename_sym, NULL);
+ if (!strcmp(filename, file)) {
result = MRB_DEBUG_BP_FILE_OK;
fix_lineno = check_lineno(info_file, lineno);
@@ -103,7 +137,7 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
}
}
for (i=0; i < irep->rlen; ++i) {
- result |= check_file_lineno(irep->reps[i], file, lineno);
+ result |= check_file_lineno(mrb, irep->reps[i], file, lineno);
if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
return result;
}
@@ -124,7 +158,7 @@ compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *cl
mrb_debug_methodpoint *method_p;
mrb_bool is_defined;
- method_name = mrb_sym2name(mrb, method_sym);
+ method_name = mrb_sym_name(mrb, method_sym);
method_p = &bp->point.methodpoint;
if (strcmp(method_p->method_name, method_name) == 0) {
@@ -149,7 +183,7 @@ compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *cl
}
sc = mrb_class_get(mrb, method_p->class_name);
- ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
+ ssym = mrb_intern_check_cstr(mrb, method_p->method_name);
m = mrb_method_search_vm(mrb, &sc, ssym);
if (MRB_METHOD_UNDEF_P(m)) {
return MRB_DEBUG_OK;
@@ -184,8 +218,8 @@ mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *fil
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);
+ /* file and lineno check. */
+ result = check_file_lineno(mrb, dbg->root_irep, file, lineno);
if (result == 0) {
return MRB_DEBUG_BREAK_INVALID_FILE;
}
@@ -193,7 +227,7 @@ mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *fil
return MRB_DEBUG_BREAK_INVALID_LINENO;
}
- set_file = (char*)mrb_malloc(mrb, strlen(file) + 1);
+ set_file = mrdb_strdup(mrb, file);
index = dbg->bpnum;
dbg->bp[index].bpno = dbg->next_bpno;
@@ -203,8 +237,6 @@ mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *fil
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;
@@ -230,16 +262,16 @@ mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *c
}
if (class_name != NULL) {
- set_class = (char*)mrb_malloc(mrb, strlen(class_name) + 1);
- strncpy(set_class, class_name, strlen(class_name) + 1);
+ set_class = mrdb_strdup(mrb, class_name);
}
else {
set_class = NULL;
}
- set_method = (char*)mrb_malloc(mrb, strlen(method_name) + 1);
-
- strncpy(set_method, method_name, strlen(method_name) + 1);
+ set_method = mrdb_strdup(mrb, method_name);
+ if (set_method == NULL) {
+ mrb_free(mrb, set_class);
+ }
index = dbg->bpnum;
dbg->bp[index].bpno = dbg->next_bpno;
@@ -426,10 +458,10 @@ mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
}
static mrb_bool
-check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
+check_start_pc_for_line(mrb_state *mrb, const mrb_irep *irep, const mrb_code *pc, uint16_t line)
{
if (pc > irep->iseq) {
- if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) {
+ if (line == mrb_debug_get_line(mrb, irep, pc - irep->iseq - 1)) {
return FALSE;
}
}
@@ -447,7 +479,7 @@ mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const ch
return MRB_DEBUG_INVALID_ARGUMENT;
}
- if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
+ if (!check_start_pc_for_line(mrb, dbg->irep, dbg->pc, line)) {
return MRB_DEBUG_OK;
}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
index 21fe64127..368ececcf 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
@@ -9,6 +9,7 @@
#include "mrdb.h"
#include "mrdberror.h"
#include "apilist.h"
+#include "apistring.h"
#include <mruby/compile.h>
#include <mruby/irep.h>
#include <mruby/debug.h>
@@ -65,7 +66,6 @@ dirname(mrb_state *mrb, const char *path)
{
size_t len;
const char *p;
- char *dir;
if (path == NULL) {
return NULL;
@@ -74,11 +74,7 @@ dirname(mrb_state *mrb, const char *path)
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;
+ return mrdb_strndup(mrb, path, len);
}
static source_file*
@@ -97,8 +93,11 @@ source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
}
file->lineno = 1;
- file->path = (char*)mrb_malloc(mrb, strlen(filename) + 1);
- strcpy(file->path, filename);
+ file->path = mrdb_strdup(mrb, filename);
+ if (file->path == NULL) {
+ source_file_free(mrb, file);
+ return NULL;
+ }
return file;
}
@@ -181,7 +180,7 @@ mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, cons
else srcname = filename;
search_path[0] = srcpath;
- search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0));
+ search_path[1] = dirname(mrb, mrb_debug_get_filename(mrb, mrdb->dbg->irep, 0));
search_path[2] = ".";
for (i = 0; i < 3; i++) {
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
index c8700530f..9093a4f0e 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
@@ -11,6 +11,7 @@
#include <mruby/error.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
+#include <mruby/presym.h>
#include "apiprint.h"
static void
@@ -31,9 +32,9 @@ mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size
}
mrb_value
-mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc)
+mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc, int direct_eval)
{
- void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
+ void (*tmp)(struct mrb_state *, const struct mrb_irep *, const mrb_code *, mrb_value *);
mrb_value ruby_code;
mrb_value s;
mrb_value v;
@@ -48,6 +49,11 @@ mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t
v = mrb_obj_value(mrb->exc);
mrb->exc = 0;
}
+ else if (direct_eval) {
+ recv = dbg->regs[0];
+
+ v = mrb_funcall(mrb, recv, expr, 0);
+ }
else {
/*
* begin
@@ -62,14 +68,14 @@ mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t
recv = dbg->regs[0];
- v = mrb_funcall(mrb, recv, "instance_eval", 1, ruby_code);
+ v = mrb_funcall_id(mrb, recv, MRB_SYM(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);
+ s = mrb_inspect(mrb, v);
/* enable code_fetch_hook */
mrb->code_fetch_hook = tmp;
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
index e256f6262..ab8c08869 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
@@ -8,6 +8,6 @@
#include <mruby.h>
#include "mrdb.h"
-mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
+mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*, int);
#endif /* APIPRINT_H_ */
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.c
new file mode 100644
index 000000000..c3844b6d2
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.c
@@ -0,0 +1,34 @@
+/*
+** apistring.c
+**
+*/
+
+#include <string.h>
+#include "apistring.h"
+
+static size_t
+mrb_debug_strnlen(const char *s, size_t maxlen)
+{
+ const char *p = memchr(s, '\0', maxlen);
+ return p != NULL ? (size_t)(p - s) : maxlen;
+}
+
+char*
+mrdb_strndup(mrb_state *mrb, const char *s, size_t size)
+{
+ size_t l = mrb_debug_strnlen(s, size);
+ char *d = mrb_malloc_simple(mrb, l + 1);
+ if (d != NULL) {
+ memcpy(d, s, l);
+ d[l] = '\0';
+ }
+ return d;
+}
+
+char*
+mrdb_strdup(mrb_state *mrb, const char *s)
+{
+ size_t z = strlen(s) + 1;
+ char *d = mrb_malloc_simple(mrb, z);
+ return d != NULL ? memcpy(d, s, z) : NULL;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.h b/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.h
new file mode 100644
index 000000000..33737e7fd
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apistring.h
@@ -0,0 +1,14 @@
+/*
+ * apistring.h
+ */
+
+#ifndef APISTRING_H_
+#define APISTRING_H_
+
+#include "mruby.h"
+
+/* both functions return a null pointer on failure */
+char *mrdb_strndup(mrb_state *mrb, const char *s, size_t size);
+char *mrdb_strdup(mrb_state *mrb, const char *s);
+
+#endif /* APISTRING_H_ */
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
index 8e5901754..bc9937e94 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
@@ -242,7 +242,7 @@ info_break_select(mrb_state *mrb, mrdb_state *mrdb)
}
mrb_debug_bptype
-parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
+parse_breakcommand(mrb_state *mrb, mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
{
mrb_debug_context *dbg = mrdb->dbg;
char *args;
@@ -274,7 +274,7 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
STRTOUL(l, body);
if (l <= 65535) {
*line = l;
- *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args;
+ *file = (body == args)? mrb_debug_get_filename(mrb, dbg->irep, dbg->pc - dbg->irep->iseq): args;
}
else {
puts(BREAK_ERR_MSG_RANGEOVER);
@@ -332,7 +332,7 @@ dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
char *method = NULL;
int32_t ret;
- type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
+ type = parse_breakcommand(mrb, mrdb, &file, &line, &cname, &method);
switch (type) {
case MRB_DEBUG_BPTYPE_LINE:
ret = mrb_debug_set_break_line(mrb, dbg, file, line);
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
index 0a864567d..9df33bc60 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
@@ -8,6 +8,7 @@
#include <string.h>
#include "apilist.h"
+#include "apistring.h"
#include <mruby/compile.h>
typedef struct help_msg {
@@ -82,6 +83,12 @@ static help_msg help_msg_list[] = {
"Arguments are breakpoint numbers with spaces in between.\n"
},
{
+ "i[nfo]", "l[ocals]", "Print name of local variables",
+ "Usage: info locals\n"
+ "\n"
+ "Print name of local variables.\n"
+ },
+ {
"l[ist]", NULL, "List specified line",
"Usage: list\n"
" list first[,last]\n"
@@ -226,10 +233,7 @@ parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st)
len = strlen(*sp);
}
- if (len > 0) {
- st->filename = (char*)mrb_malloc(mrb, len + 1);
- strncpy(st->filename, *sp, len);
- st->filename[len] = '\0';
+ if (len > 0 && (st->filename = mrdb_strndup(mrb, *sp, len)) != NULL) {
*sp += len;
return TRUE;
}
@@ -495,8 +499,8 @@ dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
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.");
+ exc = mrb_define_class(mrb, "DebuggerExit", mrb->eException_class);
+ mrb_raise(mrb, exc, "Exit mrdb");
}
return DBGST_PROMPT;
}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
index cca636711..f78c1e1fc 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
@@ -18,7 +18,6 @@ dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_value expr;
mrb_value result;
- mrb_value s;
uint8_t wcnt;
int ai;
@@ -36,11 +35,12 @@ dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
}
- result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL);
+ result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL, 0);
/* $print_no = result */
- s = mrb_str_cat_lit(mrb, result, "\0");
- printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s));
+ printf("$%lu = ", (unsigned long)mrdb->print_no++);
+ fwrite(RSTRING_PTR(result), RSTRING_LEN(result), 1, stdout);
+ putc('\n', stdout);
if (mrdb->print_no == 0) {
mrdb->print_no = 1;
@@ -56,3 +56,26 @@ dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
{
return dbgcmd_print(mrb, mrdb);
}
+
+dbgcmd_state
+dbgcmd_info_local(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_value result;
+ mrb_value s;
+ int ai;
+
+ ai = mrb_gc_arena_save(mrb);
+
+ result = mrb_debug_eval(mrb, mrdb->dbg, "local_variables", 0, NULL, 1);
+
+ 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;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
index cb4c738fc..fe8cf0aa7 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
@@ -18,9 +18,9 @@ dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
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.");
+ puts("Start it from the beginning");
+ exc = mrb_define_class(mrb, "DebuggerRestart", mrb->eException_class);
+ mrb_raise(mrb, exc, "Restart mrdb");
}
}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
index 05a6f3622..009cd955c 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
@@ -5,7 +5,6 @@
#include <stdlib.h>
#include <string.h>
-#include <stdio.h>
#include <ctype.h>
#include <mruby.h>
@@ -14,6 +13,7 @@
#include <mruby/class.h>
#include <mruby/opcode.h>
#include <mruby/variable.h>
+#include <mruby/proc.h>
#include "mrdb.h"
#include "apibreak.h"
@@ -52,6 +52,7 @@ static const debug_command debug_command_list[] = {
{"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] */
+ {"info", "locals", 1, 1, 0, DBGCMD_INFO_LOCAL, dbgcmd_info_local}, /* i[nfo] l[ocals] */
{"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] */
@@ -504,7 +505,7 @@ get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb)
}
static int32_t
-check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
+check_method_breakpoint(mrb_state *mrb, const mrb_irep *irep, const mrb_code *pc, mrb_value *regs)
{
struct RClass* c;
mrb_sym sym;
@@ -526,7 +527,7 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
sym = irep->syms[insn.b];
break;
case OP_SUPER:
- c = mrb->c->ci->target_class->super;
+ c = mrb_vm_ci_target_class(mrb->c->ci)->super;
sym = mrb->c->ci->mid;
break;
default:
@@ -545,7 +546,7 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
}
static void
-mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
+mrb_code_fetch_hook(mrb_state *mrb, const mrb_irep *irep, const mrb_code *pc, mrb_value *regs)
{
const char *file;
int32_t line;
@@ -568,8 +569,8 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
dbg->xphase = DBG_PHASE_RUNNING;
}
- file = mrb_debug_get_filename(irep, pc - irep->iseq);
- line = mrb_debug_get_line(irep, pc - irep->iseq);
+ file = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
+ line = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
switch (dbg->xm) {
case DBG_STEP:
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
index 5ac12c1cd..24ccad126 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
@@ -10,10 +10,6 @@
#include "mrdbconf.h"
-#ifdef _MSC_VER
-# define __func__ __FUNCTION__
-#endif
-
#define MAX_COMMAND_WORD (16)
typedef enum debug_command_id {
@@ -23,6 +19,7 @@ typedef enum debug_command_id {
DBGCMD_STEP,
DBGCMD_BREAK,
DBGCMD_INFO_BREAK,
+ DBGCMD_INFO_LOCAL,
DBGCMD_WATCH,
DBGCMD_INFO_WATCH,
DBGCMD_ENABLE,
@@ -102,9 +99,9 @@ typedef struct mrb_debug_breakpoint {
} mrb_debug_breakpoint;
typedef struct mrb_debug_context {
- struct mrb_irep *root_irep;
- struct mrb_irep *irep;
- mrb_code *pc;
+ const struct mrb_irep *root_irep;
+ const struct mrb_irep *irep;
+ const mrb_code *pc;
mrb_value *regs;
const char *prvfile;
@@ -151,6 +148,7 @@ 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_info_local(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*);
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
index f17f9c57d..0cc36c8b7 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
@@ -6,6 +6,14 @@
#ifndef MRDBCONF_H
#define MRDBCONF_H
+#ifndef MRB_USE_DEBUG_HOOK
+# error mruby-bin-debugger need 'MRB_USE_DEBUG_HOOK' in your build configuration
+#endif
+
+#ifdef MRB_NO_STDIO
+# error mruby-bin-debugger conflicts 'MRB_NO_STDIO' in your build configuration
+#endif
+
/* configuration options: */
/* maximum size for command buffer */
#define MAX_COMMAND_LINE 1024
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
index c7812b0d6..6f1a53f3d 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
@@ -17,4 +17,3 @@
#define MRB_DEBUG_BREAK_NO_OVER (-15)
#endif
-
diff --git a/mrbgems/mruby-bin-mirb/bintest/mirb.rb b/mrbgems/mruby-bin-mirb/bintest/mirb.rb
index 0515cb136..62f863c27 100644
--- a/mrbgems/mruby-bin-mirb/bintest/mirb.rb
+++ b/mrbgems/mruby-bin-mirb/bintest/mirb.rb
@@ -1,20 +1,21 @@
require 'open3'
+require 'tempfile'
assert('mirb normal operations') do
- o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1\nb=2\na+b\n")
+ o, s = Open3.capture2(cmd("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")
+ o, s = Open3.capture2(cmd("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")
+ o, _ = Open3.capture2(cmd("mirb"), :stdin_data => "$DEBUG\n")
assert_true o.include?('=> false')
- o, _ = Open3.capture2('bin/mirb -d', :stdin_data => "p $DEBUG\n")
+ o, _ = Open3.capture2("#{cmd('mirb')} -d", :stdin_data => "$DEBUG\n")
assert_true o.include?('=> true')
end
@@ -29,6 +30,23 @@ end
EOS
lib.flush
- o, _ = Open3.capture2("bin/mirb -r #{lib.path}", :stdin_data => "Hoge.new.hoge\n")
+ o, _ = Open3.capture2("#{cmd('mirb')} -r #{lib.path}", :stdin_data => "Hoge.new.hoge\n")
assert_true o.include?('=> :hoge')
end
+
+assert('top level local variables are in file scope') do
+ lib = Tempfile.new('lib.rb')
+ lib.write <<-TESTLIB
+a = 1
+A = -> { a }
+ TESTLIB
+ lib.flush
+
+ o, _ = Open3.capture2("#{cmd('mirb')} -r #{lib.path}", :stdin_data => <<-TESTCODE)
+a
+a = 5
+A.call
+ TESTCODE
+
+ assert_kind_of Integer, o =~ /\bundefined method 'a' \(NoMethodError\).*=> 5\b.*=> 1\b/m
+end
diff --git a/mrbgems/mruby-bin-mirb/mrbgem.rake b/mrbgems/mruby-bin-mirb/mrbgem.rake
index a74871d81..c31ac7ae9 100644
--- a/mrbgems/mruby-bin-mirb/mrbgem.rake
+++ b/mrbgems/mruby-bin-mirb/mrbgem.rake
@@ -4,7 +4,9 @@ MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec|
spec.summary = 'mirb command'
if spec.build.cc.search_header_path 'readline/readline.h'
- spec.cc.defines << "ENABLE_READLINE"
+ spec.cc.defines << "MRB_USE_READLINE"
+ spec.cc.defines << "MRB_READLINE_HEADER='<readline/readline.h>'"
+ spec.cc.defines << "MRB_READLINE_HISTORY='<readline/history.h>'"
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'
@@ -20,12 +22,26 @@ MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec|
spec.linker.libraries << 'edit'
else
spec.linker.libraries << 'readline'
+ if RUBY_PLATFORM.include?('darwin')
+ # Workaround to build with Homebrew's readline on Mac (#4537)
+ lib_path = spec.build.cc.header_search_paths.find do |include_path|
+ lib_path = File.expand_path("#{include_path}/../lib")
+ break lib_path if File.exist?("#{lib_path}/libreadline.dylib") ||
+ File.exist?("#{lib_path}/libreadline.a")
+ end
+ spec.linker.library_paths << lib_path if lib_path
+ end
if spec.build.cc.search_header_path 'curses.h'
spec.linker.libraries << 'ncurses'
end
end
+ elsif spec.build.cc.search_header_path 'edit/readline/readline.h'
+ spec.cc.defines << "MRB_USE_READLINE"
+ spec.cc.defines << "MRB_READLINE_HEADER='<edit/readline/readline.h>'"
+ spec.cc.defines << "MRB_READLINE_HISTORY='<edit/readline/history.h>'"
+ spec.linker.libraries << "edit"
elsif spec.build.cc.search_header_path 'linenoise.h'
- spec.cc.defines << "ENABLE_LINENOISE"
+ spec.cc.defines << "MRB_USE_LINENOISE"
end
spec.bins = %w(mirb)
diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
index 1880f22f7..464df1c24 100644
--- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
+++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
@@ -6,17 +6,41 @@
** immediately. It's a REPL...
*/
+#include <mruby.h>
+
+#ifdef MRB_NO_STDIO
+# error mruby-bin-mirb conflicts 'MRB_NO_STDIO' in your build configuration
+#endif
+
+#include <mruby/array.h>
+#include <mruby/proc.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/presym.h>
+
#include <stdlib.h>
#include <string.h>
-#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <setjmp.h>
+/* obsolete configuration */
#ifdef ENABLE_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
+# define MRB_USE_READLINE
+#endif
+#ifdef ENABLE_LINENOISE
+# define MRB_USE_LINENOISE
+#endif
+#ifdef DISABLE_MIRB_UNDERSCORE
+# define MRB_NO_MIRB_UNDERSCORE
+#endif
+
+#ifdef MRB_USE_READLINE
+#include MRB_READLINE_HEADER
+#include MRB_READLINE_HISTORY
#define MIRB_ADD_HISTORY(line) add_history(line)
#define MIRB_READLINE(ch) readline(ch)
#if !defined(RL_READLINE_VERSION) || RL_READLINE_VERSION < 0x600
@@ -28,8 +52,8 @@
#define MIRB_WRITE_HISTORY(path) write_history(path)
#define MIRB_READ_HISTORY(path) read_history(path)
#define MIRB_USING_HISTORY() using_history()
-#elif defined(ENABLE_LINENOISE)
-#define ENABLE_READLINE
+#elif defined(MRB_USE_LINENOISE)
+#define MRB_USE_READLINE
#include <linenoise.h>
#define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line)
#define MIRB_READLINE(ch) linenoise(ch)
@@ -39,7 +63,7 @@
#define MIRB_USING_HISTORY()
#endif
-#ifndef _WIN32
+#if !defined(_WIN32) && defined(_POSIX_C_SOURCE)
#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1)
#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val)
#define SIGJMP_BUF sigjmp_buf
@@ -49,15 +73,7 @@
#define SIGJMP_BUF jmp_buf
#endif
-#include <mruby.h>
-#include <mruby/array.h>
-#include <mruby/proc.h>
-#include <mruby/compile.h>
-#include <mruby/dump.h>
-#include <mruby/string.h>
-#include <mruby/variable.h>
-
-#ifdef ENABLE_READLINE
+#ifdef MRB_USE_READLINE
static const char history_file_name[] = ".mirb_history";
@@ -99,13 +115,13 @@ p(mrb_state *mrb, mrb_value obj, int prompt)
mrb_value val;
char* msg;
- val = mrb_funcall(mrb, obj, "inspect", 0);
+ val = mrb_funcall_id(mrb, obj, MRB_SYM(inspect), 0);
if (prompt) {
if (!mrb->exc) {
fputs(" => ", stdout);
}
else {
- val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
+ val = mrb_funcall_id(mrb, mrb_obj_value(mrb->exc), MRB_SYM(inspect), 0);
}
}
if (!mrb_string_p(val)) {
@@ -126,10 +142,6 @@ is_code_block_open(struct mrb_parser_state *parser)
/* check for heredoc */
if (parser->parsing_heredoc != NULL) return TRUE;
- if (parser->heredoc_end_now) {
- parser->heredoc_end_now = FALSE;
- return FALSE;
- }
/* check for unterminated string */
if (parser->lex_strterm) return TRUE;
@@ -199,7 +211,7 @@ is_code_block_open(struct mrb_parser_state *parser)
/* an expression was ended */
break;
case EXPR_ENDARG:
- /* closing parenthese */
+ /* closing parenthesis */
break;
case EXPR_ENDFN:
/* definition end */
@@ -233,7 +245,7 @@ usage(const char *name)
{
static const char *const usage_msg[] = {
"switches:",
- "-d set $DEBUG to true (same as `mruby -d`)"
+ "-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",
@@ -243,7 +255,7 @@ usage(const char *name)
};
const char *const *p = usage_msg;
- printf("Usage: %s [switches]\n", name);
+ printf("Usage: %s [switches] [programfile] [arguments]\n", name);
while (*p)
printf(" %s\n", *p++);
}
@@ -352,7 +364,7 @@ print_hint(void)
printf("mirb - Embeddable Interactive Ruby Shell\n\n");
}
-#ifndef ENABLE_READLINE
+#ifndef MRB_USE_READLINE
/* Print the command line prompt of the REPL */
static void
print_cmdline(int code_block_open)
@@ -376,7 +388,7 @@ check_keyword(const char *buf, const char *word)
size_t len = strlen(word);
/* skip preceding spaces */
- while (*p && isspace((unsigned char)*p)) {
+ while (*p && ISSPACE(*p)) {
p++;
}
/* check keyword */
@@ -386,14 +398,14 @@ check_keyword(const char *buf, const char *word)
p += len;
/* skip trailing spaces */
while (*p) {
- if (!isspace((unsigned char)*p)) return 0;
+ if (!ISSPACE(*p)) return 0;
p++;
}
return 1;
}
-#ifndef ENABLE_READLINE
+#ifndef MRB_USE_READLINE
volatile sig_atomic_t input_canceled = 0;
void
ctrl_c_handler(int signo)
@@ -409,12 +421,32 @@ ctrl_c_handler(int signo)
}
#endif
+#ifndef MRB_NO_MIRB_UNDERSCORE
+void decl_lv_underscore(mrb_state *mrb, mrbc_context *cxt)
+{
+ struct RProc *proc;
+ struct mrb_parser_state *parser;
+
+ parser = mrb_parse_string(mrb, "_=nil", cxt);
+ if (parser == NULL) {
+ fputs("create parser state error\n", stderr);
+ mrb_close(mrb);
+ exit(EXIT_FAILURE);
+ }
+
+ proc = mrb_generate_code(mrb, parser);
+ mrb_vm_run(mrb, proc, mrb_top_self(mrb), 0);
+
+ mrb_parser_free(parser);
+}
+#endif
+
int
main(int argc, char **argv)
{
char ruby_code[4096] = { 0 };
char last_code_line[1024] = { 0 };
-#ifndef ENABLE_READLINE
+#ifndef MRB_USE_READLINE
int last_char;
size_t char_index;
#else
@@ -458,7 +490,7 @@ main(int argc, char **argv)
mrb_define_global_const(mrb, "ARGV", ARGV);
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug));
-#ifdef ENABLE_READLINE
+#ifdef MRB_USE_READLINE
history_path = get_history_path(mrb);
if (history_path == NULL) {
fputs("failed to get history path\n", stderr);
@@ -476,6 +508,7 @@ main(int argc, char **argv)
/* Load libraries */
for (i = 0; i < args.libc; i++) {
+ struct REnv *e;
FILE *lfp = fopen(args.libv[i], "r");
if (lfp == NULL) {
printf("Cannot open library file. (%s)\n", args.libv[i]);
@@ -484,8 +517,16 @@ main(int argc, char **argv)
}
mrb_load_file_cxt(mrb, lfp, cxt);
fclose(lfp);
+ e = mrb_vm_ci_env(mrb->c->cibase);
+ mrb_vm_ci_env_set(mrb->c->cibase, NULL);
+ mrb_env_unshare(mrb, e);
+ mrbc_cleanup_local_variables(mrb, cxt);
}
+#ifndef MRB_NO_MIRB_UNDERSCORE
+ decl_lv_underscore(mrb, cxt);
+#endif
+
cxt->capture_errors = TRUE;
cxt->lineno = 1;
mrbc_filename(mrb, cxt, "(mirb)");
@@ -502,7 +543,7 @@ main(int argc, char **argv)
break;
}
-#ifndef ENABLE_READLINE
+#ifndef MRB_USE_READLINE
print_cmdline(code_block_open);
signal(SIGINT, ctrl_c_handler);
@@ -559,8 +600,7 @@ main(int argc, char **argv)
MIRB_LINE_FREE(line);
#endif
-done:
-
+ 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);
@@ -611,19 +651,18 @@ done:
/* generate bytecode */
struct RProc *proc = mrb_generate_code(mrb, parser);
if (proc == NULL) {
- fputs("codegen error\n", stderr);
mrb_parser_free(parser);
- break;
+ continue;
}
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);
+ if (mrb->c->cibase->u.env) {
+ struct REnv *e = mrb_vm_ci_env(mrb->c->cibase);
+ if (e && MRB_ENV_LEN(e) < proc->body.irep->nlocals) {
+ MRB_ENV_SET_LEN(e, proc->body.irep->nlocals);
}
}
/* pass a proc for evaluation */
@@ -640,10 +679,13 @@ done:
}
else {
/* no */
- if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){
+ if (!mrb_respond_to(mrb, result, MRB_SYM(inspect))){
result = mrb_any_to_s(mrb, result);
}
p(mrb, result, 1);
+#ifndef MRB_NO_MIRB_UNDERSCORE
+ *(mrb->c->ci->stack + 1) = result;
+#endif
}
}
ruby_code[0] = '\0';
@@ -654,7 +696,7 @@ done:
cxt->lineno++;
}
-#ifdef ENABLE_READLINE
+#ifdef MRB_USE_READLINE
MIRB_WRITE_HISTORY(history_path);
mrb_free(mrb, history_path);
#endif
diff --git a/mrbgems/mruby-compiler/bintest/mrbc.rb b/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb
index f4d9208b3..f4d9208b3 100644
--- a/mrbgems/mruby-compiler/bintest/mrbc.rb
+++ b/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb
diff --git a/mrbgems/mruby-bin-mrbc/mrbgem.rake b/mrbgems/mruby-bin-mrbc/mrbgem.rake
index e710b5a49..67200b3f0 100644
--- a/mrbgems/mruby-bin-mrbc/mrbgem.rake
+++ b/mrbgems/mruby-bin-mrbc/mrbgem.rake
@@ -2,15 +2,14 @@ MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'mruby compiler executable'
-
spec.add_dependency 'mruby-compiler', :core => 'mruby-compiler'
exec = exefile("#{build.build_dir}/bin/mrbc")
- mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }.flatten
+ mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }
- file exec => mrbc_objs + [libfile("#{build.build_dir}/lib/libmruby_core")] do |t|
+ 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' }
+ build.bins << 'mrbc'
end
diff --git a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
index 2fd9da77d..e17f32a2e 100644
--- a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
+++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
@@ -1,7 +1,11 @@
-#include <stdio.h>
+#include <mruby.h>
+
+#ifdef MRB_NO_STDIO
+# error mruby-bin-mrbc conflicts 'MRB_NO_STDIO' in your build configuration
+#endif
+
#include <stdlib.h>
#include <string.h>
-#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/dump.h>
#include <mruby/proc.h>
@@ -10,16 +14,17 @@
#define C_EXT ".c"
struct mrbc_args {
- int argc;
- char **argv;
- int idx;
const char *prog;
const char *outfile;
const char *initname;
+ char **argv;
+ int argc;
+ int idx;
+ mrb_bool dump_struct : 1;
mrb_bool check_syntax : 1;
mrb_bool verbose : 1;
mrb_bool remove_lv : 1;
- unsigned int flags : 4;
+ uint8_t flags : 4;
};
static void
@@ -28,12 +33,12 @@ usage(const char *name)
static const char *const usage_msg[] = {
"switches:",
"-c check syntax only",
- "-o<outfile> place the output into <outfile>",
+ "-o<outfile> place the output into <outfile>; required for multi-files",
"-v print version number, then turn on verbose mode",
"-g produce debugging information",
"-B<symbol> binary <symbol> output in C language format",
- "-e generate little endian iseq data",
- "-E generate big endian iseq data",
+ "-S dump C struct (requires -B)",
+ "-s define <symbol> as static variable",
"--remove-lv remove local variables",
"--verbose run at verbose mode",
"--version print the version",
@@ -42,7 +47,7 @@ usage(const char *name)
};
const char *const *p = usage_msg;
- printf("Usage: %s [switches] programfile\n", name);
+ printf("Usage: %s [switches] programfile...\n", name);
while (*p)
printf(" %s\n", *p++);
}
@@ -50,19 +55,26 @@ usage(const char *name)
static char *
get_outfilename(mrb_state *mrb, char *infile, const char *ext)
{
- size_t infilelen;
- size_t extlen;
+ size_t ilen, flen, elen;
char *outfile;
- char *p;
+ char *p = NULL;
- infilelen = strlen(infile);
- extlen = strlen(ext);
- outfile = (char*)mrb_malloc(mrb, infilelen + extlen + 1);
- memcpy(outfile, infile, infilelen + 1);
+ ilen = strlen(infile);
+ flen = ilen;
if (*ext) {
- if ((p = strrchr(outfile, '.')) == NULL)
- p = outfile + infilelen;
- memcpy(p, ext, extlen + 1);
+ elen = strlen(ext);
+ if ((p = strrchr(infile, '.'))) {
+ ilen = p - infile;
+ }
+ flen += elen;
+ }
+ else {
+ flen = ilen;
+ }
+ outfile = (char*)mrb_malloc(mrb, flen+1);
+ strncpy(outfile, infile, ilen+1);
+ if (p) {
+ strncpy(outfile+ilen, ext, elen+1);
}
return outfile;
@@ -71,7 +83,6 @@ get_outfilename(mrb_state *mrb, char *infile, const char *ext)
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;
@@ -86,7 +97,7 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
case 'o':
if (args->outfile) {
fprintf(stderr, "%s: an output file is already specified. (%s)\n",
- args->prog, outfile);
+ args->prog, args->outfile);
return -1;
}
if (argv[i][2] == '\0' && argv[i+1]) {
@@ -97,6 +108,9 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
args->outfile = get_outfilename(mrb, argv[i] + 2, "");
}
break;
+ case 'S':
+ args->dump_struct = TRUE;
+ break;
case 'B':
if (argv[i][2] == '\0' && argv[i+1]) {
i++;
@@ -118,13 +132,14 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
args->verbose = TRUE;
break;
case 'g':
- args->flags |= DUMP_DEBUG_INFO;
+ args->flags |= MRB_DUMP_DEBUG_INFO;
break;
- case 'E':
- args->flags = DUMP_ENDIAN_BIG | (args->flags & ~DUMP_ENDIAN_MASK);
+ case 's':
+ args->flags |= MRB_DUMP_STATIC;
break;
+ case 'E':
case 'e':
- args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK);
+ fprintf(stderr, "%s: -e/-E option no longer needed.\n", args->prog);
break;
case 'h':
return -1;
@@ -157,10 +172,6 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
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;
}
@@ -184,7 +195,7 @@ partial_hook(struct mrb_parser_state *p)
return -1;
}
fn = args->argv[args->idx++];
- p->f = fopen(fn, "r");
+ p->f = fopen(fn, "rb");
if (p->f == NULL) {
fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn);
return -1;
@@ -211,7 +222,7 @@ load_file(mrb_state *mrb, struct mrbc_args *args)
}
else {
need_close = TRUE;
- if ((infile = fopen(input, "r")) == NULL) {
+ if ((infile = fopen(input, "rb")) == NULL) {
fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input);
return mrb_nil_value();
}
@@ -236,13 +247,18 @@ 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;
+ const mrb_irep *irep = proc->body.irep;
if (args->remove_lv) {
- mrb_irep_remove_lv(mrb, irep);
+ mrb_irep_remove_lv(mrb, (mrb_irep*)irep);
}
if (args->initname) {
- n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
+ if (args->dump_struct) {
+ n = mrb_dump_irep_cstruct(mrb, irep, args->flags, wfp, args->initname);
+ }
+ else {
+ 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);
}
@@ -259,7 +275,7 @@ dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, st
int
main(int argc, char **argv)
{
- mrb_state *mrb = mrb_open();
+ mrb_state *mrb = mrb_open_core(NULL, NULL);
int n, result;
struct mrbc_args args;
FILE *wfp;
@@ -332,7 +348,7 @@ mrb_init_mrblib(mrb_state *mrb)
{
}
-#ifndef DISABLE_GEMS
+#ifndef MRB_NO_GEMS
void
mrb_init_mrbgems(mrb_state *mrb)
{
diff --git a/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/mrbgems/mruby-bin-mruby-config/mrbgem.rake
deleted file mode 100644
index 32ae2164b..000000000
--- a/mrbgems/mruby-bin-mruby-config/mrbgem.rake
+++ /dev/null
@@ -1,31 +0,0 @@
-module 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 => [libfile("#{build_dir}/lib/libmruby"), 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
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config b/mrbgems/mruby-bin-mruby-config/mruby-config
deleted file mode 100644
index 57346c03f..000000000
--- a/mrbgems/mruby-bin-mruby-config/mruby-config
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index a7fb63fa2..bc25f18c1 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -1,18 +1,26 @@
require 'tempfile'
+require 'open3'
+
+def assert_mruby(exp_out, exp_err, exp_success, args)
+ out, err, stat = Open3.capture3(cmd("mruby"), *args)
+ assert "assert_mruby" do
+ assert_operator(exp_out, :===, out, "standard output")
+ assert_operator(exp_err, :===, err, "standard error")
+ assert_equal(exp_success, stat.success?, "exit success?")
+ end
+end
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"
+ assert_mruby("", /\A-e:1:2: syntax error, .*\n\z/, false, %w[-e <<])
+ assert_mruby("", /\A-e:1:3: syntax error, .*\n\z/, false, %w[-e <<-])
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"'
+ o = `#{cmd('mruby')} #{bin.path}`.strip
+ assert_equal '"ok"', o
end
assert '$0 value' do
@@ -25,12 +33,17 @@ assert '$0 value' do
# .mrb file
`#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"`
- assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} -b "#{bin.path}"`.chomp
+ assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} "#{bin.path}"`.chomp
# one liner
assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp
end
+assert 'ARGV value' do
+ assert_mruby(%{["ab", "cde"]\n}, "", true, %w[-e p(ARGV) ab cde])
+ assert_mruby("[]\n", "", true, %w[-e p(ARGV)])
+end
+
assert '__END__', '8.6' do
script = Tempfile.new('test.rb')
@@ -59,11 +72,22 @@ RUBY
assert_equal 0, $?.exitstatus
end
+assert('mruby -c option') do
+ assert_mruby("Syntax OK\n", "", true, ["-c", "-e", "p 1"])
+ assert_mruby("", /\A-e:1:7: syntax error, .*\n\z/, false, ["-c", "-e", "p 1; 1."])
+end
+
assert('mruby -d option') do
- o = `#{cmd('mruby')} -e #{shellquote('p $DEBUG')}`
- assert_equal "false\n", o
- o = `#{cmd('mruby')} -d -e #{shellquote('p $DEBUG')}`
- assert_equal "true\n", o
+ assert_mruby("false\n", "", true, ["-e", "p $DEBUG"])
+ assert_mruby("true\n", "", true, ["-dep $DEBUG"])
+end
+
+assert('mruby -e option (no code specified)') do
+ assert_mruby("", /\A.*: No code specified for -e\n\z/, false, %w[-e])
+end
+
+assert('mruby -h option') do
+ assert_mruby(/\AUsage: #{Regexp.escape cmd("mruby")} .*/m, "", true, %w[-h])
end
assert('mruby -r option') do
@@ -88,3 +112,67 @@ EOS
assert_equal 'hogeClass', `#{cmd('mruby')} -r #{lib.path} -r #{script.path} -e #{shellquote('print Hoge.class')}`
assert_equal 0, $?.exitstatus
end
+
+assert('mruby -r option (no library specified)') do
+ assert_mruby("", /\A.*: No library specified for -r\n\z/, false, %w[-r])
+end
+
+assert('mruby -r option (file not found)') do
+ assert_mruby("", /\A.*: Cannot open library file: .*\n\z/, false, %w[-r _no_exists_])
+end
+
+assert('mruby -v option') do
+ ver_re = '\Amruby \d+\.\d+\.\d+.* \(\d+-\d+-\d+\)\n'
+ assert_mruby(/#{ver_re}\z/, "", true, %w[-v])
+ assert_mruby(/#{ver_re}^[^\n]*NODE.*\n:end\n\z/m, "", true, %w[-v -e p(:end)])
+end
+
+assert('mruby --verbose option') do
+ assert_mruby(/\A[^\n]*NODE.*\n:end\n\z/m, "", true, %w[--verbose -e p(:end)])
+end
+
+assert('mruby --') do
+ assert_mruby(%{["-x", "1"]\n}, "", true, %w[-e p(ARGV) -- -x 1])
+end
+
+assert('mruby invalid short option') do
+ assert_mruby("", /\A.*: invalid option -1 .*\n\z/, false, %w[-1])
+end
+
+assert('mruby invalid long option') do
+ assert_mruby("", /\A.*: invalid option --longopt .*\n\z/, false, %w[--longopt])
+end
+
+assert('unhandled exception') do
+ assert_mruby("", /\bEXCEPTION\b.*\n\z/, false, %w[-e raise("EXCEPTION")])
+end
+
+assert('program file not found') do
+ assert_mruby("", /\A.*: Cannot open program file: .*\n\z/, false, %w[_no_exists_])
+end
+
+assert('codegen error') do
+ code = "def f(#{(1..100).map{|n| "a#{n}"} * ","}); end"
+ assert_mruby("", /\A.*\n\z/, false, ["-e", code])
+end
+
+assert('top level local variables are in file scope') do
+ arb, amrb = Tempfile.new('a.rb'), Tempfile.new('a.mrb')
+ brb, bmrb = Tempfile.new('b.rb'), Tempfile.new('b.mrb')
+ crb, cmrb = Tempfile.new('c.rb'), Tempfile.new('c.mrb')
+ drb, dmrb = Tempfile.new('d.rb'), Tempfile.new('d.mrb')
+
+ File.write arb.path, 'a = 1'
+ system "#{cmd('mrbc')} -g -o #{amrb.path} #{arb.path}"
+ File.write brb.path, 'p a'
+ system "#{cmd('mrbc')} -g -o #{bmrb.path} #{brb.path}"
+ assert_mruby("", /:1: undefined method 'a' \(NoMethodError\)\n\z/, false, ["-r", arb.path, brb.path])
+ assert_mruby("", /:1: undefined method 'a' \(NoMethodError\)\n\z/, false, ["-b", "-r", amrb.path, bmrb.path])
+
+ File.write crb.path, 'a, b, c = 1, 2, 3; A = -> { b = -2; [a, b, c] }'
+ system "#{cmd('mrbc')} -g -o #{cmrb.path} #{crb.path}"
+ File.write drb.path, 'a, b = 5, 6; p A.call; p a, b'
+ system "#{cmd('mrbc')} -g -o #{dmrb.path} #{drb.path}"
+ assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-r", crb.path, drb.path])
+ assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-b", "-r", cmrb.path, dmrb.path])
+end
diff --git a/mrbgems/mruby-bin-mruby/mrbgem.rake b/mrbgems/mruby-bin-mruby/mrbgem.rake
index fbec13847..36bf2fa61 100644
--- a/mrbgems/mruby-bin-mruby/mrbgem.rake
+++ b/mrbgems/mruby-bin-mruby/mrbgem.rake
@@ -4,9 +4,9 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
spec.summary = 'mruby command'
spec.bins = %w(mruby)
spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
- spec.add_dependency('mruby-error', :core => 'mruby-error')
+ spec.add_test_dependency('mruby-print', :core => 'mruby-print')
if build.cxx_exception_enabled?
- build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx")
+ build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c")
end
end
diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
index caf8e78c2..b189aca69 100644
--- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
+++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
@@ -1,39 +1,45 @@
-#include <stdio.h>
+#include <mruby.h>
+
+#ifdef MRB_NO_STDIO
+# error mruby-bin-mruby conflicts 'MRB_NO_STDIO' in your build configuration
+#endif
+
#include <stdlib.h>
#include <string.h>
-#include <mruby.h>
#include <mruby/array.h>
#include <mruby/compile.h>
#include <mruby/dump.h>
#include <mruby/variable.h>
+#include <mruby/proc.h>
-#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)
+#if defined(_WIN32) || defined(_WIN64)
+# include <io.h> /* for setmode */
+# include <fcntl.h>
#endif
struct _args {
FILE *rfp;
- char* cmdline;
+ char *cmdline;
mrb_bool fname : 1;
mrb_bool mrbfile : 1;
mrb_bool check_syntax : 1;
mrb_bool verbose : 1;
+ mrb_bool version : 1;
mrb_bool debug : 1;
int argc;
- char** argv;
+ char **argv;
int libc;
char **libv;
};
+struct options {
+ int argc;
+ char **argv;
+ char *program;
+ char *opt;
+ char short_opt[2];
+};
+
static void
usage(const char *name)
{
@@ -41,7 +47,7 @@ usage(const char *name)
"switches:",
"-b load and execute RiteBinary (mrb) file",
"-c check syntax only",
- "-d set debugging flags (set $DEBUG to true)"
+ "-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",
@@ -52,11 +58,69 @@ usage(const char *name)
};
const char *const *p = usage_msg;
- printf("Usage: %s [switches] programfile\n", name);
+ printf("Usage: %s [switches] [programfile] [arguments]\n", name);
while (*p)
printf(" %s\n", *p++);
}
+static void
+options_init(struct options *opts, int argc, char **argv)
+{
+ opts->argc = argc;
+ opts->argv = argv;
+ opts->program = *argv;
+ *opts->short_opt = 0;
+}
+
+static const char *
+options_opt(struct options *opts)
+{
+ /* concatenated short options (e.g. `-cv`) */
+ if (*opts->short_opt && *++opts->opt) {
+ short_opt:
+ opts->short_opt[0] = *opts->opt;
+ opts->short_opt[1] = 0;
+ return opts->short_opt;
+ }
+
+ while (++opts->argv, --opts->argc) {
+ opts->opt = *opts->argv;
+
+ /* empty || not start with `-` || `-` */
+ if (!opts->opt[0] || opts->opt[0] != '-' || !opts->opt[1]) return NULL;
+
+ if (opts->opt[1] == '-') {
+ /* `--` */
+ if (!opts->opt[2]) {
+ ++opts->argv, --opts->argc;
+ return NULL;
+ }
+ /* long option */
+ opts->opt += 2;
+ *opts->short_opt = 0;
+ return opts->opt;
+ }
+ else {
+ /* short option */
+ ++opts->opt;
+ goto short_opt;
+ }
+ }
+ return NULL;
+}
+
+static const char *
+options_arg(struct options *opts)
+{
+ if (*opts->short_opt && opts->opt[1]) {
+ /* concatenated short option and option argument (e.g. `-rLIBRARY`) */
+ *opts->short_opt = 0;
+ return opts->opt + 1;
+ }
+ --opts->argc, ++opts->argv;
+ return opts->argc ? *opts->argv : NULL;
+}
+
static char *
dup_arg_item(mrb_state *mrb, const char *item)
{
@@ -69,40 +133,24 @@ dup_arg_item(mrb_state *mrb, const char *item)
static int
parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
{
- char **origargv = argv;
static const struct _args args_zero = { 0 };
+ struct options opts[1];
+ const char *opt, *item;
*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':
+ options_init(opts, argc, argv);
+ while ((opt = options_opt(opts))) {
+ if (strcmp(opt, "b") == 0) {
args->mrbfile = TRUE;
- break;
- case 'c':
+ }
+ else if (strcmp(opt, "c") == 0) {
args->check_syntax = TRUE;
- break;
- case 'd':
+ }
+ else if (strcmp(opt, "d") == 0) {
args->debug = TRUE;
- break;
- case 'e':
- if (item[0]) {
- goto append_cmdline;
- }
- else if (argc > 1) {
- argc--; argv++;
- item = argv[0];
-append_cmdline:
+ }
+ else if (strcmp(opt, "e") == 0) {
+ if ((item = options_arg(opts))) {
if (!args->cmdline) {
args->cmdline = dup_arg_item(mrb, item);
}
@@ -119,55 +167,65 @@ append_cmdline:
}
}
else {
- printf("%s: No code specified for -e\n", *origargv);
- return EXIT_SUCCESS;
+ fprintf(stderr, "%s: No code specified for -e\n", opts->program);
+ return EXIT_FAILURE;
}
- break;
- case 'r':
- if (!item[0]) {
- if (argc <= 1) {
- printf("%s: No library specified for -r\n", *origargv);
- return EXIT_FAILURE;
+ }
+ else if (strcmp(opt, "h") == 0) {
+ usage(opts->program);
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp(opt, "r") == 0) {
+ if ((item = options_arg(opts))) {
+ if (args->libc == 0) {
+ args->libv = (char**)mrb_malloc(mrb, sizeof(char*));
}
- 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);
}
else {
- args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1));
+ fprintf(stderr, "%s: No library specified for -r\n", opts->program);
+ return EXIT_FAILURE;
}
- 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) {
+ }
+ else if (strcmp(opt, "v") == 0) {
+ if (!args->verbose) {
mrb_show_version(mrb);
- exit(EXIT_SUCCESS);
+ args->version = TRUE;
}
- 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:
+ args->verbose = TRUE;
+ }
+ else if (strcmp(opt, "version") == 0) {
+ mrb_show_version(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp(opt, "verbose") == 0) {
+ args->verbose = TRUE;
+ }
+ else if (strcmp(opt, "copyright") == 0) {
+ mrb_show_copyright(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ else {
+ fprintf(stderr, "%s: invalid option %s%s (-h will show valid options)\n",
+ opts->program, opt[1] ? "--" : "-", opt);
return EXIT_FAILURE;
}
}
- if (args->rfp == NULL && args->cmdline == NULL) {
- if (*argv == NULL) args->rfp = stdin;
+ argc = opts->argc; argv = opts->argv;
+ if (args->cmdline == NULL) {
+ if (*argv == NULL) {
+ if (args->version) exit(EXIT_SUCCESS);
+ args->rfp = stdin;
+ }
else {
- args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
+ args->rfp = strcmp(argv[0], "-") == 0 ?
+ stdin : fopen(argv[0], "rb");
if (args->rfp == NULL) {
- printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
+ fprintf(stderr, "%s: Cannot open program file: %s\n", opts->program, argv[0]);
return EXIT_FAILURE;
}
args->fname = TRUE;
@@ -175,6 +233,11 @@ append_cmdline:
argc--; argv++;
}
}
+#if defined(_WIN32) || defined(_WIN64)
+ if (args->rfp == stdin) {
+ _setmode(_fileno(stdin), O_BINARY);
+ }
+#endif
args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
memcpy(args->argv, argv, (argc+1) * sizeof(char*));
args->argc = argc;
@@ -209,17 +272,15 @@ main(int argc, char **argv)
mrb_value ARGV;
mrbc_context *c;
mrb_value v;
- mrb_sym zero_sym;
if (mrb == NULL) {
- fputs("Invalid mrb_state, exiting mruby\n", stderr);
+ fprintf(stderr, "%s: Invalid mrb_state, exiting mruby\n", *argv);
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 {
@@ -242,42 +303,48 @@ main(int argc, char **argv)
c->no_exec = TRUE;
/* Set $0 */
- zero_sym = mrb_intern_lit(mrb, "$0");
+ const char *cmdline;
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"));
+ cmdline = "-e";
}
+ mrb_gv_set(mrb, mrb_intern_lit(mrb, "$0"), mrb_str_new_cstr(mrb, cmdline));
/* Load libraries */
for (i = 0; i < args.libc; i++) {
- FILE *lfp = fopen(args.libv[i], args.mrbfile ? "rb" : "r");
+ struct REnv *e;
+ FILE *lfp = fopen(args.libv[i], "rb");
if (lfp == NULL) {
- printf("Cannot open library file: %s\n", args.libv[i]);
+ fprintf(stderr, "%s: Cannot open library file: %s\n", *argv, args.libv[i]);
mrbc_context_free(mrb, c);
cleanup(mrb, &args);
return EXIT_FAILURE;
}
+ mrbc_filename(mrb, c, args.libv[i]);
if (args.mrbfile) {
v = mrb_load_irep_file_cxt(mrb, lfp, c);
}
else {
- v = mrb_load_file_cxt(mrb, lfp, c);
+ v = mrb_load_detect_file_cxt(mrb, lfp, c);
}
fclose(lfp);
+ e = mrb_vm_ci_env(mrb->c->cibase);
+ mrb_vm_ci_env_set(mrb->c->cibase, NULL);
+ mrb_env_unshare(mrb, e);
+ mrbc_cleanup_local_variables(mrb, c);
}
+ /* set program file name */
+ mrbc_filename(mrb, c, cmdline);
+
/* 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);
+ v = mrb_load_detect_file_cxt(mrb, args.rfp, c);
}
else {
char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
@@ -289,19 +356,16 @@ main(int argc, char **argv)
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 {
+ if (!mrb_undef_p(v)) {
mrb_print_error(mrb);
}
- n = -1;
+ n = EXIT_FAILURE;
}
else if (args.check_syntax) {
- printf("Syntax OK\n");
+ puts("Syntax OK");
}
}
cleanup(mrb, &args);
- return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ return n;
}
diff --git a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
index 2db3c10b1..5330510a1 100644
--- a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
+++ b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
@@ -32,7 +32,7 @@ assert('success') do
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}`
+ assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} #{compiled1.path}`
o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}`
assert_equal 0, $?.exitstatus
diff --git a/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c b/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
index fb78b0c3b..0c632b7c1 100644
--- a/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
+++ b/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
@@ -1,7 +1,11 @@
-#include <stdio.h>
+#include <mruby.h>
+
+#ifdef MRB_NO_STDIO
+# error mruby-bin-strip conflicts 'MRB_NO_STDIO' in your build configuration
+#endif
+
#include <stdlib.h>
#include <string.h>
-#include <mruby.h>
#include <mruby/irep.h>
#include <mruby/dump.h>
diff --git a/mrbgems/mruby-binding-core/mrbgem.rake b/mrbgems/mruby-binding-core/mrbgem.rake
new file mode 100644
index 000000000..c0ba48207
--- /dev/null
+++ b/mrbgems/mruby-binding-core/mrbgem.rake
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-binding-core') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Binding class (core features only)'
+
+ spec.add_test_dependency('mruby-proc-ext', :core => 'mruby-proc-ext')
+end
diff --git a/mrbgems/mruby-binding-core/src/binding-core.c b/mrbgems/mruby-binding-core/src/binding-core.c
new file mode 100644
index 000000000..7b70cfb78
--- /dev/null
+++ b/mrbgems/mruby-binding-core/src/binding-core.c
@@ -0,0 +1,306 @@
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/proc.h>
+#include <mruby/variable.h>
+#include <mruby/presym.h>
+#include <mruby/opcode.h>
+#include <mruby/debug.h>
+
+void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack);
+mrb_value mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc);
+const struct RProc *mrb_proc_get_caller(mrb_state *mrb, struct REnv **env);
+
+static mrb_int
+binding_extract_pc(mrb_state *mrb, mrb_value binding)
+{
+ mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(pc));
+ if (mrb_nil_p(obj)) {
+ return -1;
+ }
+ else {
+ mrb_check_type(mrb, obj, MRB_TT_INTEGER);
+ return mrb_int(mrb, obj);
+ }
+}
+
+const struct RProc *
+mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding)
+{
+ mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(proc));
+ mrb_check_type(mrb, obj, MRB_TT_PROC);
+ return mrb_proc_ptr(obj);
+}
+
+struct REnv *
+mrb_binding_extract_env(mrb_state *mrb, mrb_value binding)
+{
+ mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(env));
+ if (mrb_nil_p(obj)) {
+ return NULL;
+ }
+ else {
+ mrb_check_type(mrb, obj, MRB_TT_ENV);
+ return (struct REnv *)mrb_obj_ptr(obj);
+ }
+}
+
+static void
+binding_local_variable_name_check(mrb_state *mrb, mrb_sym id)
+{
+ if (id == 0) {
+ badname:
+ mrb_raisef(mrb, E_NAME_ERROR, "wrong local variable name %!n for binding", id);
+ }
+
+ mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, id, &len);
+ if (len == 0) {
+ goto badname;
+ }
+
+ if (ISASCII(*name) && !(*name == '_' || ISLOWER(*name))) {
+ goto badname;
+ }
+ len--;
+ name++;
+
+ for (; len > 0; len--, name++) {
+ if (ISASCII(*name) && !(*name == '_' || ISALNUM(*name))) {
+ goto badname;
+ }
+ }
+}
+
+static mrb_value *
+binding_local_variable_search(mrb_state *mrb, const struct RProc *proc, struct REnv *env, mrb_sym varname)
+{
+ binding_local_variable_name_check(mrb, varname);
+
+ while (proc) {
+ if (MRB_PROC_CFUNC_P(proc)) break;
+
+ const mrb_irep *irep = proc->body.irep;
+ const mrb_sym *lv;
+ if (irep && (lv = irep->lv)) {
+ for (int i = 0; i + 1 < irep->nlocals; i++, lv++) {
+ if (varname == *lv) {
+ return (env && MRB_ENV_LEN(env) > i) ? &env->stack[i + 1] : NULL;
+ }
+ }
+ }
+
+ if (MRB_PROC_SCOPE_P(proc)) break;
+ env = MRB_PROC_ENV(proc);
+ proc = proc->upper;
+ }
+
+ return NULL;
+}
+
+/*
+ * call-seq:
+ * local_variable_defined?(symbol) -> bool
+ */
+static mrb_value
+binding_local_variable_defined_p(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym varname;
+ mrb_get_args(mrb, "n", &varname);
+
+ const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
+ struct REnv *env = mrb_binding_extract_env(mrb, self);
+ mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
+ if (e) {
+ return mrb_true_value();
+ }
+ else {
+ return mrb_false_value();
+ }
+}
+
+/*
+ * call-seq:
+ * local_variable_get(symbol) -> object
+ */
+static mrb_value
+binding_local_variable_get(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym varname;
+ mrb_get_args(mrb, "n", &varname);
+
+ const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
+ struct REnv *env = mrb_binding_extract_env(mrb, self);
+ mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
+ if (!e) {
+ mrb_raisef(mrb, E_NAME_ERROR, "local variable %!n is not defined", varname);
+ }
+
+ return *e;
+}
+
+static mrb_value
+binding_local_variable_set(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym varname;
+ mrb_value obj;
+ mrb_get_args(mrb, "no", &varname, &obj);
+
+ const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
+ struct REnv *env = mrb_binding_extract_env(mrb, self);
+ mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
+ if (e) {
+ *e = obj;
+ }
+ else {
+ mrb_proc_merge_lvar(mrb, (mrb_irep*)proc->body.irep, env, 1, &varname, &obj);
+ }
+
+ return obj;
+}
+
+static mrb_value
+binding_local_variables(mrb_state *mrb, mrb_value self)
+{
+ const struct RProc *proc = mrb_proc_ptr(mrb_iv_get(mrb, self, MRB_SYM(proc)));
+ return mrb_proc_local_variables(mrb, proc);
+}
+
+static mrb_value
+binding_receiver(mrb_state *mrb, mrb_value self)
+{
+ return mrb_iv_get(mrb, self, MRB_SYM(recv));
+}
+
+/*
+ * call-seq:
+ * source_location -> [String, Integer]
+ */
+static mrb_value
+binding_source_location(mrb_state *mrb, mrb_value self)
+{
+ if (mrb_iv_defined(mrb, self, MRB_SYM(source_location))) {
+ return mrb_iv_get(mrb, self, MRB_SYM(source_location));
+ }
+
+ mrb_value srcloc;
+ const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
+ if (!proc || MRB_PROC_CFUNC_P(proc) ||
+ !proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
+ srcloc = mrb_nil_value();
+ }
+ else {
+ const mrb_irep *irep = proc->upper->body.irep;
+ mrb_int pc = binding_extract_pc(mrb, self);
+ if (pc < 0) {
+ srcloc = mrb_nil_value();
+ }
+ else {
+ const char *fname = mrb_debug_get_filename(mrb, irep, (uint32_t)pc);
+ mrb_int fline = mrb_debug_get_line(mrb, irep, (uint32_t)pc);
+
+ if (fname && fline >= 0) {
+ srcloc = mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, fname), mrb_fixnum_value(fline));
+ }
+ else {
+ srcloc = mrb_nil_value();
+ }
+ }
+ }
+
+ if (!mrb_frozen_p(mrb_obj_ptr(self))) {
+ mrb_iv_set(mrb, self, MRB_SYM(source_location), srcloc);
+ }
+ return srcloc;
+}
+
+mrb_value
+mrb_binding_alloc(mrb_state *mrb)
+{
+ struct RObject *obj = MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mrb_class_get_id(mrb, MRB_SYM(Binding)));
+ return mrb_obj_value(obj);
+}
+
+struct RProc*
+mrb_binding_wrap_lvspace(mrb_state *mrb, const struct RProc *proc, struct REnv **envp)
+{
+ /*
+ * local variable space: It is a space to hold the top-level variable of
+ * binding.eval and binding.local_variable_set.
+ */
+
+ static const mrb_code iseq_dummy[] = { OP_RETURN, 0 };
+
+ struct RProc *lvspace = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
+ mrb_irep *irep = mrb_add_irep(mrb);
+ irep->flags = MRB_ISEQ_NO_FREE;
+ irep->iseq = iseq_dummy;
+ irep->ilen = sizeof(iseq_dummy) / sizeof(iseq_dummy[0]);
+ irep->lv = (mrb_sym*)mrb_calloc(mrb, 1, sizeof(mrb_sym)); /* initial allocation for dummy */
+ irep->nlocals = 1;
+ irep->nregs = 1;
+ lvspace->body.irep = irep;
+ lvspace->upper = proc;
+ if (*envp) {
+ lvspace->e.env = *envp;
+ lvspace->flags |= MRB_PROC_ENVSET;
+ }
+
+ *envp = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, NULL);
+ (*envp)->stack = (mrb_value*)mrb_calloc(mrb, 1, sizeof(mrb_value));
+ (*envp)->stack[0] = lvspace->e.env ? lvspace->e.env->stack[0] : mrb_nil_value();
+ (*envp)->cxt = lvspace->e.env ? lvspace->e.env->cxt : mrb->c;
+ (*envp)->mid = 0;
+ (*envp)->flags = MRB_ENV_CLOSED | MRB_ENV_HEAPED;
+ MRB_ENV_SET_LEN(*envp, 1);
+
+ return lvspace;
+}
+
+static mrb_value
+mrb_f_binding(mrb_state *mrb, mrb_value self)
+{
+ mrb_value binding;
+ struct RProc *proc;
+ struct REnv *env;
+
+ binding = mrb_binding_alloc(mrb);
+ proc = (struct RProc*)mrb_proc_get_caller(mrb, &env);
+ if (!env || MRB_PROC_CFUNC_P(proc)) {
+ proc = NULL;
+ env = NULL;
+ }
+
+ if (proc && !MRB_PROC_CFUNC_P(proc)) {
+ const mrb_irep *irep = proc->body.irep;
+ mrb_iv_set(mrb, binding, MRB_SYM(pc), mrb_fixnum_value(mrb->c->ci[-1].pc - irep->iseq - 1 /* step back */));
+ }
+ proc = mrb_binding_wrap_lvspace(mrb, proc, &env);
+ mrb_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value(proc));
+ mrb_iv_set(mrb, binding, MRB_SYM(recv), self);
+ mrb_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env));
+ return binding;
+}
+
+void
+mrb_mruby_binding_core_gem_init(mrb_state *mrb)
+{
+ struct RClass *binding = mrb_define_class(mrb, "Binding", mrb->object_class);
+ mrb_undef_class_method(mrb, binding, "new");
+ mrb_undef_class_method(mrb, binding, "allocate");
+
+ mrb_define_method(mrb, mrb->kernel_module, "binding", mrb_f_binding, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, binding, "local_variable_defined?", binding_local_variable_defined_p, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, binding, "local_variable_get", binding_local_variable_get, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, binding, "local_variable_set", binding_local_variable_set, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, binding, "local_variables", binding_local_variables, MRB_ARGS_NONE());
+ mrb_define_method(mrb, binding, "receiver", binding_receiver, MRB_ARGS_NONE());
+ mrb_define_method(mrb, binding, "source_location", binding_source_location, MRB_ARGS_NONE());
+ mrb_define_method(mrb, binding, "inspect", mrb_any_to_s, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_binding_core_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-binding-core/test/binding-core.rb b/mrbgems/mruby-binding-core/test/binding-core.rb
new file mode 100644
index 000000000..066e79b18
--- /dev/null
+++ b/mrbgems/mruby-binding-core/test/binding-core.rb
@@ -0,0 +1,40 @@
+assert("Kernel.#binding") do
+ assert_kind_of Binding, binding
+end
+
+assert("Binding#local_variables") do
+ block = Proc.new do |a|
+ b = 1
+ binding
+ end
+ assert_equal [:a, :b, :block], block.call(0).local_variables.sort
+end
+
+assert("Binding#local_variable_set") do
+ bind = binding
+ 1.times {
+ assert_equal(9, bind.local_variable_set(:x, 9))
+ assert_raise(NameError) { x }
+ assert_equal([:bind, :x], bind.local_variables.sort)
+ }
+end
+
+assert("Binding#local_variable_get") do
+ bind = binding
+ x = 1
+ 1.times {
+ y = 2
+ assert_equal(1, bind.local_variable_get(:x))
+ x = 10
+ assert_equal(10, bind.local_variable_get(:x))
+ assert_raise(NameError) { bind.local_variable_get(:y) }
+ assert_equal([:bind, :x], bind.local_variables.sort)
+ }
+end
+
+assert("Binding#source_location") do
+ skip unless -> {}.source_location
+
+ bind, source_location = binding, [__FILE__, __LINE__]
+ assert_equal source_location, bind.source_location
+end
diff --git a/mrbgems/mruby-binding/mrbgem.rake b/mrbgems/mruby-binding/mrbgem.rake
new file mode 100644
index 000000000..4ad5638ea
--- /dev/null
+++ b/mrbgems/mruby-binding/mrbgem.rake
@@ -0,0 +1,11 @@
+MRuby::Gem::Specification.new('mruby-binding') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Binding class'
+
+ spec.add_dependency('mruby-binding-core', :core => 'mruby-binding-core')
+ spec.add_dependency('mruby-eval', :core => 'mruby-eval')
+ spec.add_test_dependency('mruby-metaprog', :core => 'mruby-metaprog')
+ spec.add_test_dependency('mruby-method', :core => 'mruby-method')
+ spec.add_test_dependency('mruby-proc-ext', :core => 'mruby-proc-ext')
+end
diff --git a/mrbgems/mruby-binding/src/binding.c b/mrbgems/mruby-binding/src/binding.c
new file mode 100644
index 000000000..67692e2b9
--- /dev/null
+++ b/mrbgems/mruby-binding/src/binding.c
@@ -0,0 +1,173 @@
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/error.h>
+#include <mruby/proc.h>
+#include <mruby/presym.h>
+#include <mruby/string.h>
+
+mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack);
+mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
+const struct RProc *mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding);
+struct REnv *mrb_binding_extract_env(mrb_state *mrb, mrb_value binding);
+typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user);
+void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user);
+
+static void
+insert_args(mrb_state *mrb, size_t offset, mrb_value obj)
+{
+ mrb_callinfo *ci = mrb->c->ci;
+ mrb_value *argp = ci->stack + 1 /* recv */;
+
+ if (ci->argc < 0) {
+ mrb_ary_splice(mrb, *argp, offset, 0, obj);
+ }
+ else {
+ argp += offset;
+ mrb_stack_extend(mrb, ci->argc + offset + 2 /* recv + block */);
+ memmove(argp + 1 /* obj */, argp, sizeof(mrb_value) * (ci->argc - offset + 1 /* block */));
+ *argp = obj;
+ ci->argc++;
+ }
+}
+
+static void
+binding_eval_error_check(mrb_state *mrb, struct mrb_parser_state *p, const char *file)
+{
+ if (!p) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state (out of memory)");
+ }
+
+ if (0 < p->nerr) {
+ mrb_value str;
+
+ if (file) {
+ str = mrb_format(mrb, "file %s line %d: %s",
+ file,
+ p->error_buffer[0].lineno,
+ p->error_buffer[0].message);
+ }
+ else {
+ str = mrb_format(mrb, "line %d: %s",
+ p->error_buffer[0].lineno,
+ p->error_buffer[0].message);
+ }
+ mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str));
+ }
+}
+
+#define LV_BUFFERS 8
+
+struct expand_lvspace {
+ mrb_irep *irep;
+ struct REnv *env;
+ size_t numvar;
+ mrb_sym syms[LV_BUFFERS];
+};
+
+static mrb_bool
+expand_lvspace(mrb_state *mrb, mrb_sym sym, void *user)
+{
+ struct expand_lvspace *p = (struct expand_lvspace*)user;
+ mrb_int symlen;
+ const char *symname = mrb_sym_name_len(mrb, sym, &symlen);
+
+ if (symname && symlen > 0) {
+ if (symname[0] != '&' && symname[0] != '*') {
+ p->syms[p->numvar++] = sym;
+ if (p->numvar >= LV_BUFFERS) {
+ mrb_proc_merge_lvar(mrb, p->irep, p->env, p->numvar, p->syms, NULL);
+ p->numvar = 0;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+struct binding_eval_prepare_body {
+ mrb_value binding;
+ const char *file;
+ const char *expr;
+ mrb_int exprlen;
+ mrbc_context *mrbc;
+ struct mrb_parser_state *pstate;
+};
+
+static mrb_value
+binding_eval_prepare_body(mrb_state *mrb, void *opaque)
+{
+ struct binding_eval_prepare_body *p = (struct binding_eval_prepare_body*)opaque;
+
+ const struct RProc *proc = mrb_binding_extract_proc(mrb, p->binding);
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+
+ p->mrbc = mrbc_context_new(mrb);
+ mrbc_filename(mrb, p->mrbc, p->file ? p->file : "(eval)");
+ p->mrbc->upper = proc;
+ p->mrbc->capture_errors = TRUE;
+ p->pstate = mrb_parse_nstring(mrb, p->expr, p->exprlen, p->mrbc);
+ binding_eval_error_check(mrb, p->pstate, p->file);
+
+ struct expand_lvspace args = {
+ (mrb_irep*)proc->body.irep,
+ mrb_binding_extract_env(mrb, p->binding),
+ 0,
+ { 0 }
+ };
+ mrb_parser_foreach_top_variable(mrb, p->pstate, expand_lvspace, &args);
+ if (args.numvar > 0) {
+ mrb_proc_merge_lvar(mrb, args.irep, args.env, args.numvar, args.syms, NULL);
+ }
+
+ return mrb_nil_value();
+}
+
+static void
+binding_eval_prepare(mrb_state *mrb, mrb_value binding)
+{
+ struct binding_eval_prepare_body d = { binding, NULL, NULL, 0, NULL, NULL };
+ mrb_int argc;
+ mrb_value *argv;
+ mrb_get_args(mrb, "s|z*!", &d.expr, &d.exprlen, &d.file, &argv, &argc);
+
+ mrb_bool error;
+ mrb_value ret = mrb_protect_error(mrb, binding_eval_prepare_body, &d, &error);
+ if (d.pstate) mrb_parser_free(d.pstate);
+ if (d.mrbc) mrbc_context_free(mrb, d.mrbc);
+ if (error) mrb_exc_raise(mrb, ret);
+}
+
+static mrb_value
+mrb_binding_eval(mrb_state *mrb, mrb_value binding)
+{
+ binding_eval_prepare(mrb, binding);
+
+ struct RClass *c = mrb->kernel_module;
+ mrb_method_t m = mrb_method_search_vm(mrb, &c, MRB_SYM(eval));
+ if (MRB_METHOD_UNDEF_P(m)) {
+ int argc = mrb->c->ci->argc;
+ mrb_value *argv = mrb->c->ci->stack + 1;
+ mrb_value args = (argc < 0) ? argv[0] : mrb_ary_new_from_values(mrb, argc, argv);
+ mrb_method_missing(mrb, MRB_SYM(eval), binding, args);
+ }
+
+ insert_args(mrb, 1, binding);
+ struct RProc *proc = MRB_METHOD_PROC_P(m) ? MRB_METHOD_PROC(m) : mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
+ mrb->c->ci->u.target_class = c;
+ return mrb_exec_irep(mrb, binding, proc);
+}
+
+void
+mrb_mruby_binding_gem_init(mrb_state *mrb)
+{
+ struct RClass *binding = mrb_class_get_id(mrb, MRB_SYM(Binding));
+ mrb_define_method(mrb, binding, "eval", mrb_binding_eval, MRB_ARGS_ANY());
+}
+
+void
+mrb_mruby_binding_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-binding/test/binding.c b/mrbgems/mruby-binding/test/binding.c
new file mode 100644
index 000000000..5a37ca043
--- /dev/null
+++ b/mrbgems/mruby-binding/test/binding.c
@@ -0,0 +1,13 @@
+#include <mruby.h>
+
+static mrb_value
+binding_in_c(mrb_state *mrb, mrb_value self)
+{
+ return mrb_funcall_argv(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "binding"), 0, NULL);
+}
+
+void
+mrb_mruby_binding_gem_test(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->object_class, "binding_in_c", binding_in_c, MRB_ARGS_NONE());
+}
diff --git a/mrbgems/mruby-binding/test/binding.rb b/mrbgems/mruby-binding/test/binding.rb
new file mode 100644
index 000000000..296fb48d4
--- /dev/null
+++ b/mrbgems/mruby-binding/test/binding.rb
@@ -0,0 +1,77 @@
+assert("Binding#eval") do
+ b = nil
+ 1.times { x, y, z = 1, 2, 3; [x,y,z]; b = binding }
+ assert_equal([1, 2, 3], b.eval("[x, y, z]"))
+ here = self
+ assert_equal(here, b.eval("self"))
+end
+
+assert("Binding#local_variables") do
+ block = Proc.new do |a|
+ b = 1
+ binding
+ end
+ bind = block.call(0)
+ assert_equal [:a, :b, :bind, :block], bind.local_variables.sort
+ bind.eval("x = 2")
+ assert_equal [:a, :b, :bind, :block, :x], bind.local_variables.sort
+end
+
+assert("Binding#local_variable_set") do
+ bind = binding
+ 1.times {
+ assert_equal(9, bind.local_variable_set(:x, 9))
+ assert_equal(9, bind.eval("x"))
+ assert_equal([:bind, :x], bind.eval("local_variables.sort"))
+ }
+end
+
+assert("Binding#local_variable_get") do
+ bind = binding
+ x = 1
+ 1.times {
+ y = 2
+ assert_equal(1, bind.local_variable_get(:x))
+ x = 10
+ assert_equal(10, bind.local_variable_get(:x))
+ assert_raise(NameError) { bind.local_variable_get(:y) }
+ bind.eval("z = 3")
+ assert_equal(3, bind.local_variable_get(:z))
+ bind.eval("y = 5")
+ assert_equal(5, bind.local_variable_get(:y))
+ assert_equal(2, y)
+ }
+end
+
+assert("Binding#source_location") do
+ skip unless -> {}.source_location
+
+ bind, source_location = binding, [__FILE__, __LINE__]
+ assert_equal source_location, bind.source_location
+end
+
+assert "Kernel#binding and .eval from C" do
+ bind = binding_in_c
+ assert_equal 5, bind.eval("2 + 3")
+ assert_nothing_raised { bind.eval("self") }
+end
+
+assert "Binding#eval with Binding.new via UnboundMethod" do
+ assert_raise(NoMethodError) { Class.instance_method(:new).bind_call(Binding) }
+end
+
+assert "Binding#eval with Binding.new via Method" do
+ # The following test is OK if SIGSEGV does not occur
+ cx = Class.new(Binding)
+ cx.define_singleton_method(:allocate, &Object.method(:allocate))
+ Class.instance_method(:new).bind_call(cx).eval("")
+
+ assert_true true
+end
+
+assert "access local variables into procs" do
+ bx = binding
+ block = bx.eval("a = 1; proc { a }")
+ bx.eval("a = 2")
+ assert_equal 2, block.call
+end
diff --git a/mrbgems/mruby-catch/mrbgem.rake b/mrbgems/mruby-catch/mrbgem.rake
new file mode 100644
index 000000000..c714d443d
--- /dev/null
+++ b/mrbgems/mruby-catch/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-catch') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Catch / Throw non-local Jump'
+end
diff --git a/mrbgems/mruby-catch/mrblib/catch.rb b/mrbgems/mruby-catch/mrblib/catch.rb
new file mode 100644
index 000000000..9a60a67a3
--- /dev/null
+++ b/mrbgems/mruby-catch/mrblib/catch.rb
@@ -0,0 +1,8 @@
+class UncaughtThrowError < ArgumentError
+ attr_reader :tag, :value
+ def initialize(tag, value)
+ @tag = tag
+ @value = value
+ super("uncaught throw #{tag.inspect}")
+ end
+end
diff --git a/mrbgems/mruby-catch/src/catch.c b/mrbgems/mruby-catch/src/catch.c
new file mode 100644
index 000000000..048a44738
--- /dev/null
+++ b/mrbgems/mruby-catch/src/catch.c
@@ -0,0 +1,124 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
+#include <mruby/presym.h>
+
+
+MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms_3, 1, MRB_SYM(call))
+static const mrb_code catch_iseq_3[18] = {
+ OP_ENTER, 0x00, 0x00, 0x00,
+ OP_GETUPVAR, 0x02, 0x02, 0x01,
+ OP_GETUPVAR, 0x03, 0x01, 0x01,
+ OP_SEND, 0x02, 0x00, 0x01,
+ OP_RETURN, 0x02,};
+static const mrb_irep catch_irep_3 = {
+ 2,5,0,
+ MRB_IREP_STATIC,catch_iseq_3,
+ NULL,catch_syms_3,NULL,
+ NULL,
+ NULL,
+ 18,0,1,0,0
+};
+static const mrb_irep *catch_reps_2[1] = {
+ &catch_irep_3,
+};
+static const mrb_code catch_iseq_2[13] = {
+ OP_ENTER, 0x00, 0x00, 0x00,
+ OP_LAMBDA, 0x02, 0x00,
+ OP_SEND, 0x02, 0x00, 0x00,
+ OP_RETURN, 0x02,};
+static const mrb_irep catch_irep_2 = {
+ 2,4,0,
+ MRB_IREP_STATIC,catch_iseq_2,
+ NULL,catch_syms_3,catch_reps_2,
+ NULL,
+ NULL,
+ 13,0,1,1,0
+};
+static const mrb_irep *catch_reps_1[1] = {
+ &catch_irep_2,
+};
+MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms_1, 3, MRB_SYM(Object), MRB_SYM(new), MRB_SYM(call))
+static const mrb_code catch_iseq_1[29] = {
+ OP_ENTER, 0x00, 0x20, 0x01,
+ OP_JMP, 0x00, 0x03,
+ OP_JMP, 0x00, 0x0a,
+ OP_GETCONST, 0x03, 0x00,
+ OP_SEND, 0x03, 0x01, 0x00,
+ OP_MOVE, 0x01, 0x03,
+ OP_LAMBDA, 0x03, 0x00,
+ OP_SEND, 0x03, 0x02, 0x00,
+ OP_RETURN, 0x03,};
+static const mrb_irep catch_irep = {
+ 3,5,0,
+ MRB_IREP_STATIC,catch_iseq_1,
+ NULL,catch_syms_1,catch_reps_1,
+ NULL,
+ NULL,
+ 29,0,3,1,0
+};
+
+static const struct RProc catch_proc = {
+ NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT,
+ { &catch_irep }, NULL, { NULL }
+};
+
+static const mrb_callinfo *
+find_catcher(mrb_state *mrb, mrb_value tag)
+{
+ const mrb_callinfo *ci = mrb->c->ci;
+ size_t n = ci - mrb->c->cibase;
+ ci--;
+ for (; n > 0; n--, ci--) {
+ const mrb_value *arg1 = ci->stack + 1;
+ if (ci->proc == &catch_proc && mrb_obj_eq(mrb, *arg1, tag)) {
+ return ci;
+ }
+ }
+
+ return NULL;
+}
+
+static mrb_value
+mrb_f_throw(mrb_state *mrb, mrb_value self)
+{
+ mrb_value tag, obj;
+ if (mrb_get_args(mrb, "o|o", &tag, &obj) == 1) {
+ obj = mrb_nil_value();
+ }
+
+ const mrb_callinfo *ci = find_catcher(mrb, tag);
+ if (ci) {
+ struct RBreak *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL);
+ mrb_break_value_set(b, obj);
+ mrb_break_proc_set(b, ci[2].proc); /* Back to the closure in `catch` method */
+ mrb_exc_raise(mrb, mrb_obj_value(b));
+ }
+ else {
+ mrb_value argv[2] = {tag, obj};
+ mrb_exc_raise(mrb, mrb_obj_new(mrb, mrb_exc_get_id(mrb, MRB_ERROR_SYM(UncaughtThrowError)), 2, argv));
+ }
+ /* not reached */
+ return mrb_nil_value();
+}
+
+void
+mrb_mruby_catch_gem_init(mrb_state *mrb)
+{
+ mrb_method_t m;
+
+ MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms_3);
+ MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms_1);
+ MRB_METHOD_FROM_PROC(m, &catch_proc);
+ mrb_define_method_raw(mrb, mrb->kernel_module, MRB_SYM(catch), m);
+
+ mrb_define_method(mrb, mrb->kernel_module, "throw", mrb_f_throw, MRB_ARGS_ARG(1,1));
+}
+
+void
+mrb_mruby_catch_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-catch/test/catch.rb b/mrbgems/mruby-catch/test/catch.rb
new file mode 100644
index 000000000..38a4eb907
--- /dev/null
+++ b/mrbgems/mruby-catch/test/catch.rb
@@ -0,0 +1,86 @@
+assert "return throw value" do
+ val = ["val"]
+ result = catch :foo do
+ loop do
+ loop do
+ begin
+ throw :foo, val
+ rescue Exception
+ flunk("should not reach here 1")
+ end
+ break
+ end
+ flunk("should not reach here 2")
+ end
+ false
+ end
+ assert_same(val, result)
+end
+
+assert "no throw" do
+ assert_equal(:foo, catch(:bar){:foo})
+end
+
+assert "no throw value" do
+ result = catch :foo do
+ throw :foo
+ 1
+ end
+ assert_equal(nil, result)
+end
+
+assert "pass the given tag to block" do
+ tag = [:foo]
+ catch(tag){|t| assert_same(tag, t)}
+end
+
+assert "tag identity, uncaught throw" do
+ tag, val = [:tag], [:val]
+ catch [:tag] do
+ throw tag, val
+ end
+ flunk("should not reach here")
+rescue Exception => e
+ assert_match("uncaught throw *", e.message)
+ assert_same(tag, e.tag)
+ assert_same(val, e.value)
+end
+
+assert "without catch arguments" do
+ result = catch do |tag1|
+ catch do |tag2|
+ throw tag1, 1
+ flunk("should not reach here 1")
+ end
+ flunk("should not reach here 2")
+ end
+ assert_equal(1, result)
+end
+
+assert "catches across invocation boundaries" do
+ v = []
+ catch :one do
+ v << 1
+ catch :two do
+ v << 2
+ throw :one
+ v << 3
+ end
+ v << 4
+ end
+ assert_equal([1,2], v)
+end
+
+assert "catches in the nested invocation with the same key" do
+ v = []
+ catch :tag do
+ v << 1
+ catch :tag do
+ v << 2
+ throw :tag
+ v << 3
+ end
+ v << 4
+ end
+ assert_equal([1,2,4], v)
+end
diff --git a/mrbgems/mruby-class-ext/mrblib/module.rb b/mrbgems/mruby-class-ext/mrblib/module.rb
new file mode 100644
index 000000000..301585187
--- /dev/null
+++ b/mrbgems/mruby-class-ext/mrblib/module.rb
@@ -0,0 +1,89 @@
+class Module
+
+ ##
+ # call-seq:
+ # mod < other -> true, false, or nil
+ #
+ # Returns true if `mod` is a subclass of `other`. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "A < B".)
+ #
+ def <(other)
+ if self.equal?(other)
+ false
+ else
+ self <= other
+ end
+ end
+
+ ##
+ # call-seq:
+ # mod <= other -> true, false, or nil
+ #
+ # Returns true if `mod` is a subclass of `other` or
+ # is the same as `other`. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "A < B".)
+ def <=(other)
+ raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
+ if self.ancestors.include?(other)
+ return true
+ elsif other.ancestors.include?(self)
+ return false
+ end
+ end
+
+ ##
+ # call-seq:
+ # mod > other -> true, false, or nil
+ #
+ # Returns true if `mod` is an ancestor of `other`. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "B > A".)
+ #
+ def >(other)
+ if self.equal?(other)
+ false
+ else
+ self >= other
+ end
+ end
+
+ ##
+ # call-seq:
+ # mod >= other -> true, false, or nil
+ #
+ # Returns true if `mod` is an ancestor of `other`, or the
+ # two modules are the same. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "B > A".)
+ #
+ def >=(other)
+ raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
+ return other < self
+ end
+
+ ##
+ # call-seq:
+ # module <=> other_module -> -1, 0, +1, or nil
+ #
+ # Comparison---Returns -1, 0, +1 or nil depending on whether `module`
+ # includes `other_module`, they are the same, or if `module` is included by
+ # `other_module`.
+ #
+ # Returns `nil` if `module` has no relationship with `other_module`, if
+ # `other_module` is not a module, or if the two values are incomparable.
+ #
+ def <=>(other)
+ return 0 if self.equal?(other)
+ return nil unless other.is_a?(Module)
+ cmp = self < other
+ return -1 if cmp
+ return 1 unless cmp.nil?
+ return nil
+ end
+end
diff --git a/mrbgems/mruby-class-ext/src/class.c b/mrbgems/mruby-class-ext/src/class.c
index 705db9949..39c16fc48 100644
--- a/mrbgems/mruby-class-ext/src/class.c
+++ b/mrbgems/mruby-class-ext/src/class.c
@@ -1,18 +1,22 @@
#include "mruby.h"
#include "mruby/class.h"
#include "mruby/string.h"
+#include "mruby/proc.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);
+ mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
+ if (mrb_string_p(name)) {
+ MRB_SET_FROZEN_FLAG(mrb_basic_ptr(name));
+ }
+ return 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);
+ return mrb_bool_value(mrb_sclass_p(self));
}
/*
@@ -40,14 +44,15 @@ mrb_mod_module_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);
+ mrb_get_args(mrb, "*&!", &argv, &argc, &blk);
- if (mrb_nil_p(blk)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ c = mrb_class_ptr(self);
+ if (mrb->c->ci->acc < 0) {
+ return mrb_yield_with_class(mrb, blk, argc, argv, self, c);
}
-
- mrb->c->ci->target_class = mrb_class_ptr(self);
+ mrb_vm_ci_target_class_set(mrb->c->ci, c);
return mrb_yield_cont(mrb, blk, self, argc, argv);
}
diff --git a/mrbgems/mruby-class-ext/test/module.rb b/mrbgems/mruby-class-ext/test/module.rb
index 71a8da451..52e04ab37 100644
--- a/mrbgems/mruby-class-ext/test/module.rb
+++ b/mrbgems/mruby-class-ext/test/module.rb
@@ -1,3 +1,57 @@
+assert 'Module#<' do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Module.new
+ e = Class.new { include d }
+ f = Module.new { include d }
+
+ # compare class to class
+ assert_true b < a
+ assert_false b < b
+ assert_false a < b
+ assert_nil c < b
+
+ # compare class to module
+ assert_true e < d
+ assert_false d < e
+ assert_nil a < d
+
+ # compare module to module
+ assert_true f < d
+ assert_false f < f
+ assert_false d < f
+
+ assert_raise(TypeError) { a < Object.new }
+end
+
+assert 'Module#<=' do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Module.new
+ e = Class.new { include d }
+ f = Module.new { include d }
+
+ # compare class to class
+ assert_true b <= a
+ assert_true b <= b
+ assert_false a <= b
+ assert_nil c <= b
+
+ # compare class to module
+ assert_true e <= d
+ assert_false d <= e
+ assert_nil a <= d
+
+ # compare module to module
+ assert_true f <= d
+ assert_true f <= f
+ assert_false d <= f
+
+ assert_raise(TypeError) { a <= Object.new }
+end
+
assert 'Module#name' do
module Outer
class Inner; end
diff --git a/mrbgems/mruby-cmath/mrbgem.rake b/mrbgems/mruby-cmath/mrbgem.rake
new file mode 100644
index 000000000..00ac4b091
--- /dev/null
+++ b/mrbgems/mruby-cmath/mrbgem.rake
@@ -0,0 +1,8 @@
+# This `mruby-cmath` gem uses C99 _Complex features
+# You need C compiler that support C99+
+MRuby::Gem::Specification.new('mruby-cmath') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'standard Math module with complex'
+ spec.add_dependency 'mruby-complex'
+end
diff --git a/mrbgems/mruby-cmath/src/cmath.c b/mrbgems/mruby-cmath/src/cmath.c
new file mode 100644
index 000000000..03b181840
--- /dev/null
+++ b/mrbgems/mruby-cmath/src/cmath.c
@@ -0,0 +1,270 @@
+/*
+** cmath.c - Math module with complex numbers
+**
+** See Copyright Notice in mruby.h
+*/
+
+/*
+** This `mruby-cmath` gem uses C99 _Complex features
+** You need C compiler that support C99+
+*/
+
+#include <mruby.h>
+
+#ifdef MRB_NO_FLOAT
+# error CMath conflicts with 'MRB_NO_FLOAT' configuration
+#endif
+
+#include <math.h>
+#include <complex.h>
+
+mrb_value mrb_complex_new(mrb_state *mrb, mrb_float real, mrb_float imag);
+void mrb_complex_get(mrb_state *mrb, mrb_value cpx, mrb_float*, mrb_float*);
+
+static mrb_bool
+cmath_get_complex(mrb_state *mrb, mrb_value c, mrb_float *r, mrb_float *i)
+{
+ if (mrb_integer_p(c)) {
+ *r = (mrb_float)mrb_integer(c);
+ *i = 0;
+ return FALSE;
+ }
+ else if (mrb_float_p(c)) {
+ *r = mrb_float(c);
+ *i = 0;
+ return FALSE;
+ }
+ else if (mrb_obj_is_kind_of(mrb, c, mrb_class_get(mrb, "Complex"))) {
+ mrb_complex_get(mrb, c, r, i);
+ return TRUE;
+ }
+ else {
+ mrb_raise(mrb, E_TYPE_ERROR, "Numeric required");
+ return FALSE;
+ }
+}
+
+#ifdef MRB_USE_FLOAT32
+#define F(x) x##f
+#else
+#define F(x) x
+#endif
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+#ifdef MRB_USE_FLOAT32
+typedef _Fcomplex mrb_complex;
+#define CX(r,i) _FCbuild(r,i)
+#else
+typedef _Dcomplex mrb_complex;
+#define CX(r,i) _Cbuild(r,i)
+#endif
+
+static mrb_complex
+CXDIVf(mrb_complex x, mrb_float y)
+{
+ return CX(creal(x)/y, cimag(x)/y);
+}
+
+static mrb_complex
+CXDIVc(mrb_complex a, mrb_complex b)
+{
+ mrb_float ratio, den;
+ mrb_float abr, abi, cr, ci;
+
+ if ((abr = creal(b)) < 0)
+ abr = - abr;
+ if ((abi = cimag(b)) < 0)
+ abi = - abi;
+ if (abr <= abi) {
+ ratio = creal(b) / cimag(b) ;
+ den = cimag(a) * (1 + ratio*ratio);
+ cr = (creal(a)*ratio + cimag(a)) / den;
+ ci = (cimag(a)*ratio - creal(a)) / den;
+ }
+ else {
+ ratio = cimag(b) / creal(b) ;
+ den = creal(a) * (1 + ratio*ratio);
+ cr = (creal(a) + cimag(a)*ratio) / den;
+ ci = (cimag(a) - creal(a)*ratio) / den;
+ }
+ return CX(cr, ci);
+}
+
+#else
+
+#if defined(__cplusplus) && defined(__APPLE__)
+
+#ifdef MRB_USE_FLOAT32
+typedef std::complex<float> mrb_complex;
+#else
+typedef std::complex<double> mrb_complex;
+#endif /* MRB_USE_FLOAT32 */
+
+#define CX(r,i) mrb_complex(r,i)
+#define creal(c) c.real()
+#define cimag(c) c.imag()
+#define FC(n) F(n)
+
+#else /* cpp */
+
+#ifdef MRB_USE_FLOAT32
+typedef float _Complex mrb_complex;
+#else
+typedef double _Complex mrb_complex;
+#endif /* MRB_USE_FLOAT32 */
+
+#define CX(r,i) ((r)+(i)*_Complex_I)
+#endif
+
+#define CXDIVf(x,y) (x)/(y)
+#define CXDIVc(x,y) (x)/(y)
+
+#endif
+
+#ifndef FC
+#define FC(n) F(c ## n)
+#endif
+
+#define DEF_CMATH_METHOD(name) \
+static mrb_value \
+cmath_ ## name(mrb_state *mrb, mrb_value self)\
+{\
+ mrb_value z = mrb_get_arg1(mrb);\
+ mrb_float real, imag;\
+ if (cmath_get_complex(mrb, z, &real, &imag)) {\
+ mrb_complex c = CX(real,imag);\
+ c = FC(name)(c);\
+ return mrb_complex_new(mrb, creal(c), cimag(c));\
+ }\
+ return mrb_float_value(mrb, F(name)(real));\
+}
+
+/* exp(z): return the exponential of z */
+DEF_CMATH_METHOD(exp)
+
+/* log(z): return the natural logarithm of z, with branch cut along the negative real axis */
+static mrb_value
+cmath_log(mrb_state *mrb, mrb_value self) {
+ mrb_value z;
+ mrb_float base;
+ mrb_float real, imag;
+
+ mrb_int n = mrb_get_args(mrb, "o|f", &z, &base);
+
+#ifndef M_E
+#define M_E F(exp)(1.0)
+#endif
+
+ if (n == 1) base = M_E;
+ if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) {
+ mrb_complex c = CX(real,imag);
+ c = FC(log)(c);
+ if (n == 2) c = CXDIVc(c, FC(log)(CX(base,0)));
+ return mrb_complex_new(mrb, creal(c), cimag(c));
+ }
+ if (n == 1) return mrb_float_value(mrb, F(log)(real));
+ return mrb_float_value(mrb, F(log)(real)/F(log)(base));
+}
+
+/* log10(z): return the base-10 logarithm of z, with branch cut along the negative real axis */
+static mrb_value
+cmath_log10(mrb_state *mrb, mrb_value self) {
+ mrb_value z = mrb_get_arg1(mrb);
+ mrb_float real, imag;
+ if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) {
+ mrb_complex c = CX(real,imag);
+ c = CXDIVf(FC(log)(c),log(10));
+ return mrb_complex_new(mrb, creal(c), cimag(c));
+ }
+ return mrb_float_value(mrb, F(log10)(real));
+}
+
+/* log2(z): return the base-2 logarithm of z, with branch cut along the negative real axis */
+static mrb_value
+cmath_log2(mrb_state *mrb, mrb_value self) {
+ mrb_value z = mrb_get_arg1(mrb);
+ mrb_float real, imag;
+ if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) {
+ mrb_complex c = CX(real,imag);
+ c = CXDIVf(FC(log)(c),log(2.0));
+ return mrb_complex_new(mrb, creal(c), cimag(c));
+ }
+ return mrb_float_value(mrb, F(log2)(real));
+}
+
+/* sqrt(z): return square root of z */
+static mrb_value
+cmath_sqrt(mrb_state *mrb, mrb_value self) {
+ mrb_value z = mrb_get_arg1(mrb);
+ mrb_float real, imag;
+ if (cmath_get_complex(mrb, z, &real, &imag) || real < 0.0) {
+ mrb_complex c = CX(real,imag);
+ c = FC(sqrt)(c);
+ return mrb_complex_new(mrb, creal(c), cimag(c));
+ }
+ return mrb_float_value(mrb, F(sqrt)(real));
+}
+
+/* sin(z): sine function */
+DEF_CMATH_METHOD(sin)
+/* cos(z): cosine function */
+DEF_CMATH_METHOD(cos)
+/* tan(z): tangent function */
+DEF_CMATH_METHOD(tan)
+/* asin(z): arc sine function */
+DEF_CMATH_METHOD(asin)
+/* acos(z): arc cosine function */
+DEF_CMATH_METHOD(acos)
+/* atan(z): arg tangent function */
+DEF_CMATH_METHOD(atan)
+/* sinh(z): hyperbolic sine function */
+DEF_CMATH_METHOD(sinh)
+/* cosh(z): hyperbolic cosine function */
+DEF_CMATH_METHOD(cosh)
+/* tanh(z): hyperbolic tangent function */
+DEF_CMATH_METHOD(tanh)
+/* asinh(z): inverse hyperbolic sine function */
+DEF_CMATH_METHOD(asinh)
+/* acosh(z): inverse hyperbolic cosine function */
+DEF_CMATH_METHOD(acosh)
+/* atanh(z): inverse hyperbolic tangent function */
+DEF_CMATH_METHOD(atanh)
+
+/* ------------------------------------------------------------------------*/
+
+void
+mrb_mruby_cmath_gem_init(mrb_state* mrb)
+{
+ struct RClass *cmath;
+ cmath = mrb_define_module(mrb, "CMath");
+
+ mrb_include_module(mrb, cmath, mrb_module_get(mrb, "Math"));
+
+ mrb_define_module_function(mrb, cmath, "sin", cmath_sin, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "cos", cmath_cos, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "tan", cmath_tan, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, cmath, "asin", cmath_asin, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "acos", cmath_acos, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "atan", cmath_atan, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, cmath, "sinh", cmath_sinh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "cosh", cmath_cosh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "tanh", cmath_tanh, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, cmath, "asinh", cmath_asinh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "acosh", cmath_acosh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "atanh", cmath_atanh, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, cmath, "exp", cmath_exp, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "log", cmath_log, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_module_function(mrb, cmath, "log2", cmath_log2, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "log10", cmath_log10, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cmath, "sqrt", cmath_sqrt, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_cmath_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-cmath/test/cmath.rb b/mrbgems/mruby-cmath/test/cmath.rb
new file mode 100644
index 000000000..ddd0e5106
--- /dev/null
+++ b/mrbgems/mruby-cmath/test/cmath.rb
@@ -0,0 +1,41 @@
+##
+# CMath Test
+
+def assert_complex(c1, c2)
+ assert('assert_complex') do
+ assert_float(c1.real, c2.real)
+ assert_float(c1.imaginary, c2.imaginary)
+ end
+end
+
+assert('CMath.exp') do
+ assert_float(1.0, CMath.exp(0))
+ assert_complex(-1+0i, CMath.exp(Math::PI.i))
+ assert_complex((-1.1312043837568135+2.4717266720048188i), CMath.exp(1+2i))
+end
+
+assert('CMath.log') do
+ assert_float(0, CMath.log(1))
+ assert_float(3.0, CMath.log(8,2))
+ assert_complex((1.092840647090816-0.42078724841586035i), CMath.log(-8,-2))
+end
+
+assert('CMath.sqrt') do
+ assert_complex(Complex(0,2), CMath.sqrt(-4.0))
+ assert_complex(Complex(0,3), CMath.sqrt(-9.0))
+end
+
+assert('CMath trigonometric_functions') do
+ assert_complex(Math.sinh(2).i, CMath.sin(2i))
+ assert_complex(Math.cosh(2)+0i, CMath.cos(2i))
+ assert_complex(Math.tanh(2).i, CMath.tan(2i))
+ assert_complex(Math.sin(2).i, CMath.sinh(2i))
+ assert_complex(Math.cos(2)+0i, CMath.cosh(2i))
+ assert_complex(Math.tan(2).i, CMath.tanh(2i))
+ assert_complex(1+1i, CMath.sin(CMath.asin(1+1i)))
+ assert_complex(1+1i, CMath.cos(CMath.acos(1+1i)))
+ assert_complex(1+1i, CMath.tan(CMath.atan(1+1i)))
+ assert_complex(1+1i, CMath.sinh(CMath.asinh(1+1i)))
+ assert_complex(1+1i, CMath.cosh(CMath.acosh(1+1i)))
+ assert_complex(1+1i, CMath.tanh(CMath.atanh(1+1i)))
+end
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index 8ab1b9bc3..5b779b63d 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -5,15 +5,17 @@
*/
#include <ctype.h>
-#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/proc.h>
+#include <mruby/dump.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
#include <mruby/debug.h>
+#include <mruby/presym.h>
#include "node.h"
#include <mruby/opcode.h>
#include <mruby/re.h>
@@ -38,8 +40,8 @@ enum looptype {
struct loopinfo {
enum looptype type;
- int pc0, pc1, pc2, pc3, acc;
- int ensure_level;
+ uint32_t pc0, pc1, pc2, pc3;
+ int acc;
struct loopinfo *prev;
};
@@ -53,15 +55,14 @@ typedef struct scope {
node *lv;
uint16_t sp;
- uint16_t pc;
- uint16_t lastpc;
- uint16_t lastlabel;
+ uint32_t pc;
+ uint32_t lastpc;
+ uint32_t lastlabel;
int ainfo:15;
mrb_bool mscope:1;
struct loopinfo *loop;
- int ensure_level;
- char const *filename;
+ mrb_sym filename_sym;
uint16_t lineno;
mrb_code *iseq;
@@ -69,6 +70,10 @@ typedef struct scope {
uint32_t icapa;
mrb_irep *irep;
+ mrb_pool_value *pool;
+ mrb_sym *syms;
+ mrb_irep **reps;
+ struct mrb_irep_catch_handler *catch_table;
uint32_t pcapa, scapa, rcapa;
uint16_t nlocals;
@@ -88,6 +93,15 @@ 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);
+/*
+ * The search for catch handlers starts at the end of the table in mrb_vm_run().
+ * Therefore, the next handler to be added must meet one of the following conditions.
+ * - Larger start position
+ * - Same start position but smaller end position
+ */
+static int catch_handler_new(codegen_scope *s);
+static void catch_handler_set(codegen_scope *s, int ent, enum mrb_catch_type type, uint32_t begin, uint32_t end, uint32_t target);
+
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);
@@ -100,16 +114,37 @@ 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);
+ if (s->irep) {
+ mrb_free(s->mrb, s->iseq);
+ for (int i=0; i<s->irep->plen; i++) {
+ mrb_pool_value *pv = &s->pool[i];
+ if ((pv->tt & 0x3) == IREP_TT_STR || pv->tt == IREP_TT_BIGINT) {
+ mrb_free(s->mrb, (void*)pv->u.str);
+ }
+ }
+ mrb_free(s->mrb, s->pool);
+ mrb_free(s->mrb, s->syms);
+ mrb_free(s->mrb, s->catch_table);
+ if (s->reps) {
+ /* copied from mrb_irep_free() in state.c */
+ for (int i=0; i<s->irep->rlen; i++) {
+ if (s->reps[i])
+ mrb_irep_decref(s->mrb, (mrb_irep*)s->reps[i]);
+ }
+ mrb_free(s->mrb, s->reps);
+ }
+ mrb_free(s->mrb, s->lines);
+ }
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);
+#ifndef MRB_NO_STDIO
+ if (s->filename_sym && s->lineno) {
+ const char *filename = mrb_sym_name_len(s->mrb, s->filename_sym, NULL);
+ fprintf(stderr, "%s:%d: %s\n", filename, s->lineno, message);
}
else {
- fprintf(stderr, "codegen error: %s\n", message);
+ fprintf(stderr, "%s\n", message);
}
#endif
MRB_THROW(&s->jmp);
@@ -125,15 +160,6 @@ codegen_palloc(codegen_scope *s, size_t len)
}
static void*
-codegen_malloc(codegen_scope *s, size_t len)
-{
- void *p = mrb_malloc_simple(s->mrb, len);
-
- if (!p) codegen_error(s, "mrb_malloc");
- return p;
-}
-
-static void*
codegen_realloc(codegen_scope *s, void *p, size_t len)
{
p = mrb_realloc_simple(s->mrb, p, len);
@@ -151,22 +177,26 @@ new_label(codegen_scope *s)
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;
+ if (pc == UINT32_MAX) {
+ codegen_error(s, "too big code block");
+ }
+ if (pc >= UINT32_MAX / 2) {
+ pc = UINT32_MAX;
+ }
+ else {
+ s->icapa *= 2;
}
s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
if (s->lines) {
s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->icapa);
- s->irep->lines = s->lines;
}
}
if (s->lines) {
- s->lines[pc] = s->lineno;
+ if (s->lineno > 0 || pc == 0)
+ s->lines[pc] = s->lineno;
+ else
+ s->lines[pc] = s->lines[pc-1];
}
s->iseq[pc] = i;
}
@@ -261,6 +291,14 @@ genop_2S(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
}
static void
+genop_2SS(codegen_scope *s, mrb_code i, uint16_t a, uint32_t b)
+{
+ genop_1(s, i, a);
+ gen_S(s, b>>16);
+ gen_S(s, b&0xffff);
+}
+
+static void
genop_W(codegen_scope *s, mrb_code i, uint32_t a)
{
uint8_t a1 = (a>>16) & 0xff;
@@ -277,8 +315,7 @@ genop_W(codegen_scope *s, mrb_code i, uint32_t a)
#define NOVAL 0
#define VAL 1
-//static
-mrb_bool
+static mrb_bool
no_optimize(codegen_scope *s)
{
if (s && s->parser && s->parser->no_optimize)
@@ -286,23 +323,14 @@ no_optimize(codegen_scope *s)
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)
+mrb_decode_insn(const 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;
+ uint16_t c = 0;
switch (insn) {
#define FETCH_Z() /* empty */
@@ -363,22 +391,44 @@ 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)
+#define JMPLINK_START UINT32_MAX
+
+static void
+gen_jmpdst(codegen_scope *s, uint32_t pc)
+{
+
+ if (pc == JMPLINK_START) {
+ gen_S(s, 0);
+ }
+ else {
+ uint32_t pos2 = s->pc+2;
+ int32_t off = pc - pos2;
+
+ if (off > INT16_MAX || INT16_MIN > off) {
+ codegen_error(s, "too big jump offset");
+ }
+ gen_S(s, (uint16_t)off);
+ }
+}
+
+static uint32_t
+genjmp(codegen_scope *s, mrb_code i, uint32_t pc)
{
- uint16_t pos;
+ uint32_t pos;
s->lastpc = s->pc;
gen_B(s, i);
pos = s->pc;
- gen_S(s, pc);
+ gen_jmpdst(s, pc);
return pos;
}
-static uint16_t
-genjmp2(codegen_scope *s, mrb_code i, uint16_t a, int pc, int val)
+#define genjmp_0(s,i) genjmp(s,i,JMPLINK_START)
+
+static uint32_t
+genjmp2(codegen_scope *s, mrb_code i, uint16_t a, uint32_t pc, int val)
{
- uint16_t pos;
+ uint32_t pos;
if (!no_peephole(s) && !val) {
struct mrb_insn_data data = mrb_last_insn(s);
@@ -394,27 +444,24 @@ genjmp2(codegen_scope *s, mrb_code i, uint16_t a, int pc, int val)
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);
}
+ pos = s->pc;
+ gen_jmpdst(s, pc);
return pos;
}
+#define genjmp2_0(s,i,a,val) genjmp2(s,i,a,JMPLINK_START,val)
+
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 {
@@ -434,7 +481,8 @@ gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
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_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:
@@ -468,11 +516,11 @@ gen_return(codegen_scope *s, uint8_t op, uint16_t src)
}
static void
-gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst, uint16_t idx)
+gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
{
if (no_peephole(s)) {
normal:
- genop_2(s, op, dst, idx);
+ genop_1(s, op, dst);
return;
}
else {
@@ -493,10 +541,10 @@ gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst, uint16_t idx)
if (data.b >= 128) goto normal;
s->pc = s->lastpc;
if (op == OP_ADD) {
- genop_3(s, OP_ADDI, dst, idx, (uint8_t)data.b);
+ genop_2(s, OP_ADDI, dst, (uint8_t)data.b);
}
else {
- genop_3(s, OP_SUBI, dst, idx, (uint8_t)data.b);
+ genop_2(s, OP_SUBI, dst, (uint8_t)data.b);
}
break;
default:
@@ -505,21 +553,31 @@ gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst, uint16_t idx)
}
}
-static int
-dispatch(codegen_scope *s, uint16_t pos0)
+static uint32_t
+dispatch(codegen_scope *s, uint32_t pos0)
{
- uint16_t newpos;
+ int32_t pos1;
+ int32_t offset;
+ int16_t newpos;
+
+ if (pos0 == JMPLINK_START) return 0;
+ pos1 = pos0 + 2;
+ offset = s->pc - pos1;
+ if (offset > INT16_MAX) {
+ codegen_error(s, "too big jmp offset");
+ }
s->lastlabel = s->pc;
- newpos = PEEK_S(s->iseq+pos0);
- emit_S(s, pos0, s->pc);
- return newpos;
+ newpos = (int16_t)PEEK_S(s->iseq+pos0);
+ emit_S(s, pos0, (uint16_t)offset);
+ if (newpos == 0) return 0;
+ return pos1+newpos;
}
static void
-dispatch_linked(codegen_scope *s, uint16_t pos)
+dispatch_linked(codegen_scope *s, uint32_t pos)
{
- if (pos==0) return;
+ if (pos==JMPLINK_START) return;
for (;;) {
pos = dispatch(s, pos);
if (pos==0) break;
@@ -552,38 +610,91 @@ pop_n_(codegen_scope *s, int n)
#define pop_n(n) pop_n_(s,n)
#define cursp() (s->sp)
-static inline int
+static int
+new_litbn(codegen_scope *s, const char *p, int base, mrb_bool neg)
+{
+ int i;
+ size_t plen;
+ mrb_pool_value *pv;
+
+ plen = strlen(p);
+ if (plen > 255) {
+ codegen_error(s, "integer too big");
+ }
+ for (i=0; i<s->irep->plen; i++) {
+ size_t len;
+ pv = &s->pool[i];
+ if (pv->tt != IREP_TT_BIGINT) continue;
+ len = pv->u.str[0];
+ if (len == plen && pv->u.str[1] == base && memcmp(pv->u.str+2, p, len) == 0)
+ return i;
+ }
+
+ if (s->irep->plen == s->pcapa) {
+ s->pcapa *= 2;
+ s->pool = (mrb_pool_value*)codegen_realloc(s, s->pool, sizeof(mrb_pool_value)*s->pcapa);
+ }
+
+ pv = &s->pool[s->irep->plen];
+ i = s->irep->plen++;
+ {
+ char *buf;
+ pv->tt = IREP_TT_BIGINT;
+ buf = (char*)codegen_realloc(s, NULL, plen+3);
+ buf[0] = (char)plen;
+ buf[1] = base;
+ if (neg) buf[1] = 0x80;
+ memcpy(buf+2, p, plen);
+ buf[plen+2] = '\0';
+ pv->u.str = buf;
+ }
+ return i;
+}
+
+static int
new_lit(codegen_scope *s, mrb_value val)
{
int i;
- mrb_value *pv;
+ mrb_pool_value *pv;
switch (mrb_type(val)) {
case MRB_TT_STRING:
for (i=0; i<s->irep->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)
+ pv = &s->pool[i];
+ if (pv->tt & IREP_TT_NFLAG) continue;
+ len = pv->tt>>2;
+ if (RSTRING_LEN(val) != len) continue;
+ if (memcmp(pv->u.str, RSTRING_PTR(val), len) == 0)
return i;
}
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
for (i=0; i<s->irep->plen; i++) {
- pv = &s->irep->pool[i];
- if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
- if (mrb_float(*pv) == mrb_float(val)) return i;
+ mrb_float f1, f2;
+ pv = &s->pool[i];
+ if (pv->tt != IREP_TT_FLOAT) continue;
+ pv = &s->pool[i];
+ f1 = pv->u.f;
+ f2 = mrb_float(val);
+ if (f1 == f2 && !signbit(f1) == !signbit(f2)) return i;
}
break;
#endif
- case MRB_TT_FIXNUM:
+ case MRB_TT_INTEGER:
for (i=0; i<s->irep->plen; i++) {
- pv = &s->irep->pool[i];
- if (!mrb_fixnum_p(*pv)) continue;
- if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i;
+ mrb_int v = mrb_integer(val);
+ pv = &s->pool[i];
+ if (pv->tt == IREP_TT_INT32) {
+ if (v == pv->u.i32) return i;
+ }
+#ifdef MRB_64BIT
+ else if (pv->tt == IREP_TT_INT64) {
+ if (v == pv->u.i64) return i;
+ }
+ continue;
+#endif
}
break;
default:
@@ -593,26 +704,43 @@ new_lit(codegen_scope *s, mrb_value val)
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);
+ s->pool = (mrb_pool_value*)codegen_realloc(s, s->pool, sizeof(mrb_pool_value)*s->pcapa);
}
- pv = &s->irep->pool[s->irep->plen];
+ pv = &s->pool[s->irep->plen];
i = s->irep->plen++;
switch (mrb_type(val)) {
case MRB_TT_STRING:
- *pv = mrb_str_pool(s->mrb, val);
+ if (RSTR_NOFREE_P(RSTRING(val))) {
+ pv->tt = (uint32_t)(RSTRING_LEN(val)<<2) | IREP_TT_SSTR;
+ pv->u.str = RSTRING_PTR(val);
+ }
+ else {
+ char *p;
+ mrb_int len = RSTRING_LEN(val);
+ pv->tt = (uint32_t)(len<<2) | IREP_TT_STR;
+ p = (char*)codegen_realloc(s, NULL, len+1);
+ memcpy(p, RSTRING_PTR(val), len);
+ p[len] = '\0';
+ pv->u.str = p;
+ }
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
-#ifdef MRB_WORD_BOXING
- *pv = mrb_float_pool(s->mrb, mrb_float(val));
+ pv->tt = IREP_TT_FLOAT;
+ pv->u.f = mrb_float(val);
break;
#endif
+ case MRB_TT_INTEGER:
+#ifdef MRB_INT64
+ pv->tt = IREP_TT_INT64;
+ pv->u.i64 = mrb_integer(val);
+#else
+ pv->tt = IREP_TT_INT32;
+ pv->u.i32 = mrb_integer(val);
#endif
- case MRB_TT_FIXNUM:
- *pv = val;
break;
default:
@@ -622,9 +750,6 @@ new_lit(codegen_scope *s, mrb_value val)
return i;
}
-/* maximum symbol numbers */
-#define MAXSYMLEN 0x10000
-
static int
new_sym(codegen_scope *s, mrb_sym sym)
{
@@ -634,13 +759,16 @@ new_sym(codegen_scope *s, mrb_sym sym)
len = s->irep->slen;
for (i=0; i<len; i++) {
- if (s->irep->syms[i] == sym) return i;
+ if (s->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);
+ if (s->scapa > 0xffff) {
+ codegen_error(s, "too many symbols");
+ }
+ s->syms = (mrb_sym*)codegen_realloc(s, s->syms, sizeof(mrb_sym)*s->scapa);
}
- s->irep->syms[s->irep->slen] = sym;
+ s->syms[s->irep->slen] = sym;
return s->irep->slen++;
}
@@ -676,6 +804,47 @@ lv_idx(codegen_scope *s, mrb_sym id)
return 0;
}
+static int
+search_upvar(codegen_scope *s, mrb_sym id, int *idx)
+{
+ const struct RProc *u;
+ int lv = 0;
+ codegen_scope *up = s->prev;
+
+ while (up) {
+ *idx = lv_idx(up, id);
+ if (*idx > 0) {
+ return lv;
+ }
+ lv ++;
+ up = up->prev;
+ }
+
+ if (lv < 1) lv = 1;
+ u = s->parser->upper;
+ while (u && !MRB_PROC_CFUNC_P(u)) {
+ const struct mrb_irep *ir = u->body.irep;
+ uint_fast16_t n = ir->nlocals;
+ int i;
+
+ const mrb_sym *v = ir->lv;
+ if (v) {
+ for (i=1; n > 1; n--, v++, i++) {
+ if (*v == id) {
+ *idx = i;
+ return lv - 1;
+ }
+ }
+ }
+ if (MRB_PROC_SCOPE_P(u)) break;
+ u = u->upper;
+ lv ++;
+ }
+
+ codegen_error(s, "Can't found local variables");
+ return -1; /* not reached */
+}
+
static void
for_body(codegen_scope *s, node *tree)
{
@@ -688,9 +857,6 @@ for_body(codegen_scope *s, node *tree)
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 */
@@ -717,7 +883,7 @@ for_body(codegen_scope *s, node *tree)
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"));
+ idx = new_sym(s, MRB_SYM_2(s->mrb, each));
genop_3(s, OP_SENDB, cursp(), idx, 0);
}
@@ -726,9 +892,6 @@ 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;
@@ -737,27 +900,30 @@ lambda_body(codegen_scope *s, node *tree, int blk)
lp->pc0 = new_label(s);
}
tree = tree->cdr;
- if (tree->car) {
+ if (tree->car == NULL) {
+ genop_W(s, OP_ENTER, 0);
+ s->ainfo = 0;
+ }
+ else {
mrb_aspec a;
int ma, oa, ra, pa, ka, kd, ba;
int pos, i;
- node *n, *opt;
+ node *opt;
+ node *margs, *pargs;
node *tail;
/* mandatory arguments */
ma = node_len(tree->car->car);
- n = tree->car->car;
- while (n) {
- n = n->cdr;
- }
+ 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 */
+ /* mandatory arguments 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? */
@@ -777,27 +943,34 @@ lambda_body(codegen_scope *s, node *tree, int blk)
s->ainfo = (((ma+oa) & 0x3f) << 7) /* (12bits = 5:1:5:1) */
| ((ra & 0x1) << 6)
| ((pa & 0x1f) << 1)
- | (kd & 0x1);
+ | ((ka | kd) != 0 ? 0x01 : 0x00);
genop_W(s, OP_ENTER, a);
/* generate jump table for optional arguments initializer */
pos = new_label(s);
for (i=0; i<oa; i++) {
new_label(s);
- genjmp(s, OP_JMP, 0);
+ genjmp_0(s, OP_JMP);
}
if (oa > 0) {
- genjmp(s, OP_JMP, 0);
+ genjmp_0(s, OP_JMP);
}
opt = tree->car->cdr->car;
i = 0;
while (opt) {
int idx;
+ mrb_sym id = nsym(opt->car->car);
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);
+ idx = lv_idx(s, id);
+ if (idx > 0) {
+ gen_move(s, idx, cursp(), 0);
+ }
+ else {
+ int lv = search_upvar(s, id, &idx);
+ genop_3(s, OP_GETUPVAR, cursp(), idx, lv);
+ }
i++;
opt = opt->cdr;
}
@@ -805,6 +978,7 @@ lambda_body(codegen_scope *s, node *tree, int blk)
dispatch(s, pos+i*3+1);
}
+ /* keyword arguments */
if (tail) {
node *kwds = tail->cdr->car;
int kwrest = 0;
@@ -823,12 +997,20 @@ lambda_body(codegen_scope *s, node *tree, int blk)
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);
+ int idx;
+ genop_2(s, OP_KEY_P, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+ jmpif_key_p = genjmp2_0(s, OP_JMPIF, lv_idx(s, kwd_sym), NOVAL);
codegen(s, def_arg, VAL);
pop();
- gen_move(s, lv_idx(s, kwd_sym), cursp(), 0);
- jmp_def_set = genjmp(s, OP_JMP, 0);
+ idx = lv_idx(s, kwd_sym);
+ if (idx > 0) {
+ gen_move(s, idx, cursp(), 0);
+ }
+ else {
+ int lv = search_upvar(s, kwd_sym, &idx);
+ genop_3(s, OP_GETUPVAR, cursp(), idx, lv);
+ }
+ jmp_def_set = genjmp_0(s, OP_JMP);
dispatch(s, jmpif_key_p);
}
genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
@@ -843,7 +1025,34 @@ lambda_body(codegen_scope *s, node *tree, int blk)
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) {
@@ -860,9 +1069,6 @@ 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);
@@ -894,7 +1100,7 @@ attrsym(codegen_scope *s, mrb_sym a)
mrb_int len;
char *name2;
- name = mrb_sym2name_len(s->mrb, a, &len);
+ name = mrb_sym_name_len(s->mrb, a, &len);
name2 = (char *)codegen_palloc(s,
(size_t)len
+ 1 /* '=' */
@@ -928,7 +1134,12 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
}
else {
pop_n(n);
- genop_2(s, OP_ARRAY, cursp(), n);
+ if (n == 0 && is_splat) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ genop_2(s, OP_ARRAY, cursp(), n);
+ }
push();
codegen(s, t->car, VAL);
pop(); pop();
@@ -973,16 +1184,15 @@ 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 idx, skip = 0;
+ 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);
+ skip = genjmp2_0(s, OP_JMPNIL, cursp(), val);
}
- idx = new_sym(s, sym);
tree = tree->cdr->cdr->car;
if (tree) {
n = gen_values(s, tree->car, VAL, sp?1:0);
@@ -1014,36 +1224,38 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
pop_n(n+1);
{
mrb_int symlen;
- const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
+ const char *symname = mrb_sym_name_len(s->mrb, sym, &symlen);
if (!noop && symlen == 1 && symname[0] == '+' && n == 1) {
- gen_addsub(s, OP_ADD, cursp(), idx);
+ gen_addsub(s, OP_ADD, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '-' && n == 1) {
- gen_addsub(s, OP_SUB, cursp(), idx);
+ gen_addsub(s, OP_SUB, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '*' && n == 1) {
- genop_2(s, OP_MUL, cursp(), idx);
+ genop_1(s, OP_MUL, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '/' && n == 1) {
- genop_2(s, OP_DIV, cursp(), idx);
+ genop_1(s, OP_DIV, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '<' && n == 1) {
- genop_2(s, OP_LT, cursp(), idx);
+ genop_1(s, OP_LT, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1) {
- genop_2(s, OP_LE, cursp(), idx);
+ genop_1(s, OP_LE, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '>' && n == 1) {
- genop_2(s, OP_GT, cursp(), idx);
+ genop_1(s, OP_GT, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1) {
- genop_2(s, OP_GE, cursp(), idx);
+ genop_1(s, OP_GE, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1) {
- genop_2(s, OP_EQ, cursp(), idx);
+ 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);
}
@@ -1072,30 +1284,24 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
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;
- }
+ int lv = search_upvar(s, nsym(tree), &idx);
+ genop_3(s, OP_SETUPVAR, sp, idx, lv);
}
break;
+ case NODE_NVAR:
+ idx = nint(tree);
+ codegen_error(s, "Can't assign to numbered parameter");
+ break;
case NODE_IVAR:
idx = new_sym(s, nsym(tree));
genop_2(s, OP_SETIV, sp, idx);
@@ -1137,9 +1343,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
break;
default:
-#ifndef MRB_DISABLE_STDIO
- fprintf(stderr, "unknown lhs %d\n", type);
-#endif
+ codegen_error(s, "unknown lhs");
break;
}
if (val) push();
@@ -1179,7 +1383,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
pop_n(post+1);
genop_3(s, OP_APOST, cursp(), n, post);
n = 1;
- if (t->car) { /* rest */
+ if (t->car && t->car != (node*)-1) { /* rest */
gen_assignment(s, t->car, cursp(), NOVAL);
}
if (t->cdr && t->cdr->car) {
@@ -1266,70 +1470,37 @@ raise_error(codegen_scope *s, const char *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<base; n++) {
- if (mrb_digitmap[n] == c) {
- f *= base;
- f += n;
- break;
- }
- }
- if (n == base) {
- codegen_error(s, "malformed readint input");
- }
- p++;
- }
- return f;
-}
-#endif
-
static mrb_int
-readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
+readint(codegen_scope *s, const char *p, int base, mrb_bool *overflow)
{
const char *e = p + strlen(p);
mrb_int result = 0;
- int n;
mrb_assert(base >= 2 && base <= 36);
if (*p == '+') p++;
while (p < e) {
+ int n;
char c = *p;
- c = tolower((unsigned char)c);
- for (n=0; n<base; n++) {
- if (mrb_digitmap[n] == c) {
- break;
- }
- }
- if (n == base) {
+ switch (c) {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ n = c - '0'; break;
+ case '8': case '9':
+ n = c - '0'; break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ n = c - 'a' + 10; break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ n = c - 'A' + 10; break;
+ default:
codegen_error(s, "malformed readint input");
+ *overflow = TRUE;
+ /* not reached */
+ return result;
}
-
- if (neg) {
- if ((MRB_INT_MIN + n)/base > 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;
+ if (mrb_int_mul_overflow(result, base, &result) ||
+ mrb_int_add_overflow(result, n, &result)) {
+ *overflow = TRUE;
+ return 0;
}
p++;
}
@@ -1370,11 +1541,14 @@ codegen(codegen_scope *s, node *tree, int val)
codegen_error(s, "too complex expression");
}
if (s->irep && s->filename_index != tree->filename_index) {
- s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
- mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
+ mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+ const char *filename = mrb_sym_name_len(s->mrb, fname, NULL);
+
+ 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);
+ s->filename_sym = mrb_parser_get_filename(s->parser, tree->filename_index);
}
nt = nint(tree->car);
@@ -1394,21 +1568,25 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_RESCUE:
{
- int noexc, exend, pos1, pos2, tmp;
+ int noexc;
+ uint32_t exend, pos1, pos2, tmp;
struct loopinfo *lp;
+ int catch_entry, begin, end;
if (tree->car == NULL) goto exit;
lp = loop_push(s, LOOP_BEGIN);
lp->pc0 = new_label(s);
- lp->pc1 = genjmp(s, OP_ONERR, 0);
+ catch_entry = catch_handler_new(s);
+ begin = s->pc;
codegen(s, tree->car, VAL);
pop();
lp->type = LOOP_RESCUE;
- noexc = genjmp(s, OP_JMP, 0);
- dispatch(s, lp->pc1);
+ end = s->pc;
+ noexc = genjmp_0(s, OP_JMP);
+ catch_handler_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc);
tree = tree->cdr;
- exend = 0;
- pos1 = 0;
+ exend = JMPLINK_START;
+ pos1 = JMPLINK_START;
if (tree->car) {
node *n2 = tree->car;
int exc = cursp();
@@ -1419,22 +1597,22 @@ codegen(codegen_scope *s, node *tree, int val)
node *n3 = n2->car;
node *n4 = n3->car;
- if (pos1) dispatch(s, pos1);
- pos2 = 0;
+ dispatch(s, pos1);
+ pos2 = JMPLINK_START;
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);
+ genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(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")));
+ genop_2(s, OP_GETCONST, cursp(), new_sym(s, MRB_SYM_2(s->mrb, StandardError)));
push();
}
pop();
@@ -1446,7 +1624,7 @@ codegen(codegen_scope *s, node *tree, int val)
n4 = n4->cdr;
}
} while (n4);
- pos1 = genjmp(s, OP_JMP, 0);
+ pos1 = genjmp_0(s, OP_JMP);
dispatch_linked(s, pos2);
pop();
@@ -1462,15 +1640,14 @@ codegen(codegen_scope *s, node *tree, int val)
n2 = n2->cdr;
push();
}
- if (pos1) {
+ if (pos1 != JMPLINK_START) {
dispatch(s, pos1);
- genop_1(s, OP_RAISE, exc);
+ genop_1(s, OP_RAISEIF, exc);
}
}
pop();
tree = tree->cdr;
dispatch(s, noexc);
- genop_1(s, OP_POPERR, 1);
if (tree->car) {
codegen(s, tree->car, val);
}
@@ -1486,14 +1663,22 @@ codegen(codegen_scope *s, node *tree, int val)
if (!tree->cdr || !tree->cdr->cdr ||
(nint(tree->cdr->cdr->car) == NODE_BEGIN &&
tree->cdr->cdr->cdr)) {
+ int catch_entry, begin, end, target;
int idx;
- s->ensure_level++;
- idx = scope_body(s, tree->cdr, NOVAL);
- genop_1(s, OP_EPUSH, idx);
+ catch_entry = catch_handler_new(s);
+ begin = s->pc;
codegen(s, tree->car, val);
- s->ensure_level--;
- genop_1(s, OP_EPOP, 1);
+ end = target = s->pc;
+ push();
+ idx = cursp();
+ genop_1(s, OP_EXCEPT, idx);
+ push();
+ codegen(s, tree->cdr->cdr, NOVAL);
+ pop();
+ genop_1(s, OP_RAISEIF, idx);
+ pop();
+ catch_handler_set(s, catch_entry, MRB_CATCH_ENSURE, begin, end, target);
}
else { /* empty ensure ignored */
codegen(s, tree->car, val);
@@ -1520,7 +1705,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_IF:
{
- int pos1, pos2;
+ int pos1, pos2, nil_p = FALSE;
node *elsepart = tree->cdr->cdr->car;
if (!tree->car) {
@@ -1537,32 +1722,59 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_NIL:
codegen(s, elsepart, val);
goto exit;
+ case NODE_CALL:
+ {
+ node *n = tree->car->cdr;
+ mrb_sym mid = nsym(n->cdr->car);
+ mrb_sym mnil = MRB_SYM_Q_2(s->mrb, nil);
+ if (mid == mnil && n->cdr->cdr->car == NULL) {
+ nil_p = TRUE;
+ codegen(s, n->car, VAL);
+ }
+ }
+ break;
+ }
+ if (!nil_p) {
+ codegen(s, tree->car, VAL);
}
- codegen(s, tree->car, VAL);
pop();
- pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
-
- codegen(s, tree->cdr->car, val);
- if (elsepart) {
+ if (val || tree->cdr->car) {
+ if (nil_p) {
+ pos2 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
+ pos1 = genjmp_0(s, OP_JMP);
+ dispatch(s, pos2);
+ }
+ else {
+ pos1 = genjmp2_0(s, OP_JMPNOT, cursp(), val);
+ }
+ codegen(s, tree->cdr->car, val);
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);
+ if (elsepart || val) {
+ pos2 = genjmp_0(s, OP_JMP);
dispatch(s, pos1);
- genop_1(s, OP_LOADNIL, cursp());
+ codegen(s, elsepart, val);
dispatch(s, pos2);
- push();
}
else {
dispatch(s, pos1);
}
}
+ else { /* empty then-part */
+ if (elsepart) {
+ if (nil_p) {
+ pos1 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
+ }
+ else {
+ pos1 = genjmp2_0(s, OP_JMPIF, cursp(), val);
+ }
+ codegen(s, elsepart, val);
+ dispatch(s, pos1);
+ }
+ else if (val && !nil_p) {
+ genop_1(s, OP_LOADNIL, cursp());
+ push();
+ }
+ }
}
break;
@@ -1572,7 +1784,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- pos = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
+ pos = genjmp2_0(s, OP_JMPNOT, cursp(), val);
codegen(s, tree->cdr, val);
dispatch(s, pos);
}
@@ -1584,7 +1796,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- pos = genjmp2(s, OP_JMPIF, cursp(), 0, val);
+ pos = genjmp2_0(s, OP_JMPIF, cursp(), val);
codegen(s, tree->cdr, val);
dispatch(s, pos);
}
@@ -1595,7 +1807,7 @@ codegen(codegen_scope *s, node *tree, int val)
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
lp->pc0 = new_label(s);
- lp->pc1 = genjmp(s, OP_JMP, 0);
+ lp->pc1 = genjmp_0(s, OP_JMP);
lp->pc2 = new_label(s);
codegen(s, tree->cdr, NOVAL);
dispatch(s, lp->pc1);
@@ -1612,7 +1824,7 @@ codegen(codegen_scope *s, node *tree, int val)
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
lp->pc0 = new_label(s);
- lp->pc1 = genjmp(s, OP_JMP, 0);
+ lp->pc1 = genjmp_0(s, OP_JMP);
lp->pc2 = new_label(s);
codegen(s, tree->cdr, NOVAL);
dispatch(s, lp->pc1);
@@ -1632,10 +1844,10 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_CASE:
{
int head = 0;
- int pos1, pos2, pos3, tmp;
+ uint32_t pos1, pos2, pos3, tmp;
node *n;
- pos3 = 0;
+ pos3 = JMPLINK_START;
if (tree->car) {
head = cursp();
codegen(s, tree->car, VAL);
@@ -1643,17 +1855,17 @@ codegen(codegen_scope *s, node *tree, int val)
tree = tree->cdr;
while (tree) {
n = tree->car->car;
- pos1 = pos2 = 0;
+ pos1 = pos2 = JMPLINK_START;
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);
+ genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, __case_eqq)), 1);
}
else {
- genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "===")), 1);
+ genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, eqq)), 1);
}
}
else {
@@ -1664,20 +1876,20 @@ codegen(codegen_scope *s, node *tree, int val)
n = n->cdr;
}
if (tree->car->car) {
- pos1 = genjmp(s, OP_JMP, 0);
+ pos1 = genjmp_0(s, OP_JMP);
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);
+ dispatch(s, pos1);
tree = tree->cdr;
}
if (val) {
- int pos = cursp();
+ uint32_t pos = cursp();
genop_1(s, OP_LOADNIL, cursp());
- if (pos3) dispatch_linked(s, pos3);
+ if (pos3 != JMPLINK_START) dispatch_linked(s, pos3);
if (head) pop();
if (cursp() != pos) {
gen_move(s, cursp(), pos, 0);
@@ -1685,7 +1897,7 @@ codegen(codegen_scope *s, node *tree, int val)
push();
}
else {
- if (pos3) {
+ if (pos3 != JMPLINK_START) {
dispatch_linked(s, pos3);
}
if (head) {
@@ -1800,7 +2012,7 @@ codegen(codegen_scope *s, node *tree, int val)
len++;
}
tree = tree->cdr;
- if (val && len == 255) {
+ if (val && cursp() > 127) {
pop_n(len*2);
if (!update) {
genop_2(s, OP_HASH, cursp(), len);
@@ -1922,24 +2134,26 @@ codegen(codegen_scope *s, node *tree, int val)
{
mrb_sym sym = nsym(tree->cdr->car);
mrb_int len;
- const char *name = mrb_sym2name_len(s->mrb, sym, &len);
+ const char *name = mrb_sym_name_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;
+ int catch_entry, begin, end;
+ int noexc, exc;
struct loopinfo *lp;
- onerr = genjmp(s, OP_ONERR, 0);
lp = loop_push(s, LOOP_BEGIN);
- lp->pc1 = onerr;
+ lp->pc0 = new_label(s);
+ catch_entry = catch_handler_new(s);
+ begin = s->pc;
exc = cursp();
codegen(s, tree->car, VAL);
+ end = s->pc;
+ noexc = genjmp_0(s, OP_JMP);
lp->type = LOOP_RESCUE;
- genop_1(s, OP_POPERR, 1);
- noexc = genjmp(s, OP_JMP, 0);
- dispatch(s, onerr);
+ catch_handler_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc);
genop_1(s, OP_EXCEPT, exc);
genop_1(s, OP_LOADF, exc);
dispatch(s, noexc);
@@ -1990,10 +2204,10 @@ codegen(codegen_scope *s, node *tree, int val)
if (vsp >= 0) {
gen_move(s, vsp, cursp(), 1);
}
- pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
+ pos = genjmp2_0(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), val);
}
else {
- pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
+ pos = genjmp2_0(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), val);
}
codegen(s, tree->cdr->cdr->car, VAL);
pop();
@@ -2023,32 +2237,32 @@ codegen(codegen_scope *s, node *tree, int val)
push(); pop();
pop(); pop();
- idx = new_sym(s, sym);
if (len == 1 && name[0] == '+') {
- gen_addsub(s, OP_ADD, cursp(), idx);
+ gen_addsub(s, OP_ADD, cursp());
}
else if (len == 1 && name[0] == '-') {
- gen_addsub(s, OP_SUB, cursp(), idx);
+ gen_addsub(s, OP_SUB, cursp());
}
else if (len == 1 && name[0] == '*') {
- genop_2(s, OP_MUL, cursp(), idx);
+ genop_1(s, OP_MUL, cursp());
}
else if (len == 1 && name[0] == '/') {
- genop_2(s, OP_DIV, cursp(), idx);
+ genop_1(s, OP_DIV, cursp());
}
else if (len == 1 && name[0] == '<') {
- genop_2(s, OP_LT, cursp(), idx);
+ genop_1(s, OP_LT, cursp());
}
else if (len == 2 && name[0] == '<' && name[1] == '=') {
- genop_2(s, OP_LE, cursp(), idx);
+ genop_1(s, OP_LE, cursp());
}
else if (len == 1 && name[0] == '>') {
- genop_2(s, OP_GT, cursp(), idx);
+ genop_1(s, OP_GT, cursp());
}
else if (len == 2 && name[0] == '>' && name[1] == '=') {
- genop_2(s, OP_GE, cursp(), idx);
+ genop_1(s, OP_GE, cursp());
}
else {
+ idx = new_sym(s, sym);
genop_3(s, OP_SEND, cursp(), idx, 1);
}
if (callargs < 0) {
@@ -2124,7 +2338,9 @@ codegen(codegen_scope *s, node *tree, int val)
s2 = s2->prev;
if (!s2) break;
}
- if (s2) ainfo = s2->ainfo;
+ if (s2 && s2->ainfo > 0) {
+ ainfo = s2->ainfo;
+ }
genop_2S(s, OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf));
push(); push(); pop(); /* ARGARY pushes two values */
if (tree && tree->cdr) {
@@ -2156,7 +2372,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_YIELD:
{
codegen_scope *s2 = s;
- int lv = 0, ainfo = 0;
+ int lv = 0, ainfo = -1;
int n = 0, sendv = 0;
while (!s2->mscope) {
@@ -2164,7 +2380,10 @@ codegen(codegen_scope *s, node *tree, int val)
s2 = s2->prev;
if (!s2) break;
}
- if (s2) ainfo = s2->ainfo;
+ if (s2) {
+ ainfo = s2->ainfo;
+ }
+ if (ainfo < 0) codegen_error(s, "invalid yield (SyntaxError)");
push();
if (tree) {
n = gen_values(s, tree, VAL, 0);
@@ -2177,7 +2396,7 @@ codegen(codegen_scope *s, node *tree, int val)
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);
+ genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, call)), n);
if (val) push();
}
break;
@@ -2192,11 +2411,8 @@ codegen(codegen_scope *s, node *tree, int val)
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);
+ genjmp(s, OP_JMPUW, s->loop->pc0);
}
else {
if (tree) {
@@ -2216,10 +2432,7 @@ codegen(codegen_scope *s, node *tree, int val)
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);
+ genjmp(s, OP_JMPUW, s->loop->pc2);
}
if (val) push();
break;
@@ -2227,32 +2440,16 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_RETRY:
{
const char *msg = "unexpected retry";
+ const struct loopinfo *lp = s->loop;
- if (!s->loop) {
+ while (lp && lp->type != LOOP_RESCUE) {
+ lp = lp->prev;
+ }
+ if (!lp) {
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);
- }
+ genjmp(s, OP_JMPUW, lp->pc0);
}
if (val) push();
}
@@ -2264,26 +2461,25 @@ codegen(codegen_scope *s, node *tree, int val)
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;
- }
+ int lv = search_upvar(s, nsym(tree), &idx);
+ genop_3(s, OP_GETUPVAR, cursp(), idx, lv);
}
push();
}
break;
+ case NODE_NVAR:
+ if (val) {
+ int idx = nint(tree);
+
+ gen_move(s, cursp(), idx, val);
+
+ push();
+ }
+ break;
+
case NODE_GVAR:
{
int sym = new_sym(s, nsym(tree));
@@ -2320,19 +2516,11 @@ codegen(codegen_scope *s, node *tree, int val)
}
break;
- case NODE_DEFINED:
- codegen(s, tree, val);
- break;
-
case NODE_BACK_REF:
if (val) {
- char buf[3];
- int sym;
+ char buf[] = {'$', nchar(tree)};
+ int sym = new_sym(s, mrb_intern(s->mrb, buf, sizeof(buf)));
- 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();
}
@@ -2344,7 +2532,7 @@ codegen(codegen_scope *s, node *tree, int val)
mrb_value str;
int sym;
- str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
+ str = mrb_format(mrb, "$%d", nint(tree));
sym = new_sym(s, mrb_intern_str(mrb, str));
genop_2(s, OP_GETGV, cursp(), sym);
push();
@@ -2366,23 +2554,27 @@ codegen(codegen_scope *s, node *tree, int val)
mrb_int i;
mrb_bool overflow;
- i = readint_mrb_int(s, p, base, FALSE, &overflow);
-#ifndef MRB_WITHOUT_FLOAT
+ i = readint(s, p, base, &overflow);
if (overflow) {
- double f = readint_float(s, p, base);
- int off = new_lit(s, mrb_float_value(s->mrb, f));
-
+ int off = new_litbn(s, p, base, FALSE);
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 < 0) {
+ if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+ else if (i >= -0xff) genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
+ else if (i >= INT16_MIN) genop_2S(s, OP_LOADI16, cursp(), (uint16_t)i);
+ else if (i >= INT32_MIN) genop_2SS(s, OP_LOADI32, cursp(), (uint32_t)i);
+ else goto lit_int;
+ }
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 if (i <= 0xff) genop_2(s, OP_LOADI, cursp(), (uint16_t)i);
+ else if (i <= INT16_MAX) genop_2S(s, OP_LOADI16, cursp(), (uint16_t)i);
+ else if (i <= INT32_MAX) genop_2SS(s, OP_LOADI32, cursp(), (uint32_t)i);
else {
- int off = new_lit(s, mrb_fixnum_value(i));
+ int off;
+ lit_int:
+ off = new_lit(s, mrb_int_value(s->mrb, i));
genop_2(s, OP_LOADL, cursp(), off);
}
}
@@ -2390,7 +2582,7 @@ codegen(codegen_scope *s, node *tree, int val)
}
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case NODE_FLOAT:
if (val) {
char *p = (char*)tree;
@@ -2407,7 +2599,7 @@ codegen(codegen_scope *s, node *tree, int val)
{
nt = nint(tree->car);
switch (nt) {
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case NODE_FLOAT:
if (val) {
char *p = (char*)tree->cdr;
@@ -2427,34 +2619,35 @@ codegen(codegen_scope *s, node *tree, int val)
mrb_int i;
mrb_bool overflow;
- i = readint_mrb_int(s, p, base, TRUE, &overflow);
-#ifndef MRB_WITHOUT_FLOAT
+ i = readint(s, p, base, &overflow);
if (overflow) {
- double f = readint_float(s, p, base);
- int off = new_lit(s, mrb_float_value(s->mrb, -f));
-
+ int off = new_litbn(s, p, base, TRUE);
genop_2(s, OP_LOADL, cursp(), off);
}
else {
-#endif
+ i = -i;
if (i == -1) genop_1(s, OP_LOADI__1, cursp());
- else if (i >= -0xffff) {
+ else if (i >= -0xff) {
genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
}
+ else if (i >= INT16_MIN) {
+ genop_2S(s, OP_LOADI16, cursp(), (uint16_t)i);
+ }
+ else if (i >= INT32_MIN) {
+ genop_2SS(s, OP_LOADI32, cursp(), (uint32_t)i);
+ }
else {
- int off = new_lit(s, mrb_fixnum_value(i));
+ int off = new_lit(s, mrb_int_value(s->mrb, 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, "-@"));
+ int sym = new_sym(s, MRB_OPSYM_2(s->mrb, minus));
codegen(s, tree, VAL);
pop();
genop_3(s, OP_SEND, cursp(), sym, 0);
@@ -2527,7 +2720,7 @@ codegen(codegen_scope *s, node *tree, int val)
{
node *n;
int ai = mrb_gc_arena_save(s->mrb);
- int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
+ int sym = new_sym(s, MRB_SYM_2(s->mrb, Kernel));
genop_1(s, OP_LOADSELF, cursp());
push();
@@ -2546,7 +2739,7 @@ codegen(codegen_scope *s, node *tree, int val)
}
push(); /* for block */
pop_n(3);
- sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
+ sym = new_sym(s, MRB_OPSYM_2(s->mrb, tick)); /* ` */
genop_3(s, OP_SEND, cursp(), sym, 1);
if (val) push();
mrb_gc_arena_restore(s->mrb, ai);
@@ -2566,7 +2759,7 @@ codegen(codegen_scope *s, node *tree, int val)
genop_2(s, OP_STRING, cursp(), off);
push(); push();
pop_n(3);
- sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
+ sym = new_sym(s, MRB_OPSYM_2(s->mrb, tick)); /* ` */
genop_3(s, OP_SEND, cursp(), sym, 1);
if (val) push();
mrb_gc_arena_restore(s->mrb, ai);
@@ -2607,7 +2800,7 @@ codegen(codegen_scope *s, node *tree, int val)
}
push(); /* space for a block */
pop_n(argc+2);
- sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
+ sym = new_sym(s, MRB_SYM_2(s->mrb, compile));
genop_3(s, OP_SEND, cursp(), sym, argc);
mrb_gc_arena_restore(s->mrb, ai);
push();
@@ -2661,7 +2854,7 @@ codegen(codegen_scope *s, node *tree, int val)
}
push(); /* space for a block */
pop_n(argc+2);
- sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
+ sym = new_sym(s, MRB_SYM_2(s->mrb, compile));
genop_3(s, OP_SEND, cursp(), sym, argc);
mrb_gc_arena_restore(s->mrb, ai);
push();
@@ -2778,7 +2971,10 @@ codegen(codegen_scope *s, node *tree, int val)
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)) {
+ if (nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
idx = scope_body(s, body, val);
genop_2(s, OP_EXEC, cursp(), idx);
}
@@ -2806,8 +3002,11 @@ codegen(codegen_scope *s, node *tree, int 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)) {
+ if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+ tree->cdr->car->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
idx = scope_body(s, tree->cdr->car, val);
genop_2(s, OP_EXEC, cursp(), idx);
}
@@ -2824,8 +3023,11 @@ codegen(codegen_scope *s, node *tree, int val)
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)) {
+ if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+ tree->cdr->car->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
idx = scope_body(s, tree->cdr->car, val);
genop_2(s, OP_EXEC, cursp(), idx);
}
@@ -2846,10 +3048,7 @@ codegen(codegen_scope *s, node *tree, int val)
push(); pop();
pop();
genop_2(s, OP_DEF, cursp(), sym);
- if (val) {
- genop_2(s, OP_LOADSYM, cursp(), sym);
- push();
- }
+ if (val) push();
}
break;
@@ -2866,10 +3065,7 @@ codegen(codegen_scope *s, node *tree, int val)
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();
- }
+ if (val) push();
}
break;
@@ -2885,97 +3081,100 @@ codegen(codegen_scope *s, node *tree, int val)
}
static void
-scope_add_irep(codegen_scope *s, mrb_irep *irep)
+scope_add_irep(codegen_scope *s)
{
- if (s->irep == NULL) {
- s->irep = irep;
+ mrb_irep *irep;
+ codegen_scope *prev = s->prev;
+
+ if (prev->irep == NULL) {
+ irep = mrb_add_irep(s->mrb);
+ prev->irep = 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);
+ else {
+ if (prev->irep->rlen == UINT16_MAX) {
+ codegen_error(s, "too many nested blocks/methods");
+ }
+ s->irep = irep = mrb_add_irep(s->mrb);
+ if (prev->irep->rlen == prev->rcapa) {
+ prev->rcapa *= 2;
+ prev->reps = (mrb_irep**)codegen_realloc(s, prev->reps, sizeof(mrb_irep*)*prev->rcapa);
+ }
+ prev->reps[prev->irep->rlen] = irep;
+ prev->irep->rlen++;
}
- s->irep->reps[s->irep->rlen] = irep;
- s->irep->rlen++;
}
static codegen_scope*
-scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
+scope_new(mrb_state *mrb, codegen_scope *prev, node *nlv)
{
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;
+ codegen_scope *s = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope));
+
+ if (!s) {
+ if (prev)
+ codegen_error(prev, "unexpected scope");
+ return NULL;
+ }
+ *s = codegen_scope_zero;
+ s->mrb = mrb;
+ s->mpool = pool;
+ if (!prev) return s;
+ s->prev = prev;
+ s->ainfo = -1;
+ s->mscope = 0;
+
+ scope_add_irep(s);
+
+ s->rcapa = 8;
+ s->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*s->rcapa);
+
+ s->icapa = 1024;
+ s->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*s->icapa);
+
+ s->pcapa = 32;
+ s->pool = (mrb_pool_value*)mrb_malloc(mrb, sizeof(mrb_pool_value)*s->pcapa);
+
+ s->scapa = 256;
+ s->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*s->scapa);
+
+ s->lv = nlv;
+ s->sp += node_len(nlv)+1; /* add self */
+ s->nlocals = s->sp;
+ if (nlv) {
+ mrb_sym *lv;
+ node *n = nlv;
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;
- }
+ s->irep->lv = lv = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*(s->nlocals-1));
+ for (i=0, n=nlv; n; i++,n=n->cdr) {
+ lv[i] = lv_name(n);
}
- mrb_assert(i + 1 == p->nlocals);
+ mrb_assert(i + 1 == s->nlocals);
}
- p->ai = mrb_gc_arena_save(mrb);
+ s->ai = mrb_gc_arena_save(mrb);
- p->filename = prev->filename;
- if (p->filename) {
- p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa);
+ s->filename_sym = prev->filename_sym;
+ if (s->filename_sym) {
+ s->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*s->icapa);
}
- p->lineno = prev->lineno;
+ s->lineno = prev->lineno;
/* debug setting */
- p->debug_start_pos = 0;
- if (p->filename) {
- mrb_debug_info_alloc(mrb, p->irep);
- p->irep->filename = p->filename;
- p->irep->lines = p->lines;
+ s->debug_start_pos = 0;
+ if (s->filename_sym) {
+ mrb_debug_info_alloc(mrb, s->irep);
}
else {
- p->irep->debug_info = NULL;
+ s->irep->debug_info = NULL;
}
- p->parser = prev->parser;
- p->filename_index = prev->filename_index;
+ s->parser = prev->parser;
+ s->filename_index = prev->filename_index;
- p->rlev = prev->rlev+1;
+ s->rlev = prev->rlev+1;
- return p;
+ return s;
}
static void
@@ -2983,34 +3182,35 @@ scope_finish(codegen_scope *s)
{
mrb_state *mrb = s->mrb;
mrb_irep *irep = s->irep;
- size_t fname_len;
- char *fname;
+ if (s->nlocals > 0xff) {
+ codegen_error(s, "too many local variables");
+ }
irep->flags = 0;
if (s->iseq) {
- irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
+ size_t catchsize = sizeof(struct mrb_irep_catch_handler) * irep->clen;
+ irep->iseq = (const mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc + catchsize);
irep->ilen = s->pc;
- if (s->lines) {
- irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc);
- }
- else {
- irep->lines = 0;
+ if (irep->clen > 0) {
+ memcpy((void *)(irep->iseq + irep->ilen), s->catch_table, catchsize);
}
}
- 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) {
- irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
- mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc);
+ else {
+ irep->clen = 0;
+ }
+ mrb_free(s->mrb, s->catch_table);
+ s->catch_table = NULL;
+ irep->pool = (const mrb_pool_value*)codegen_realloc(s, s->pool, sizeof(mrb_pool_value)*irep->plen);
+ irep->syms = (const mrb_sym*)codegen_realloc(s, s->syms, sizeof(mrb_sym)*irep->slen);
+ irep->reps = (const mrb_irep**)codegen_realloc(s, s->reps, sizeof(mrb_irep*)*irep->rlen);
+ if (s->filename_sym) {
+ mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+ const char *filename = mrb_sym_name_len(s->mrb, fname, NULL);
- fname_len = strlen(s->filename);
- fname = (char*)codegen_malloc(s, fname_len + 1);
- memcpy(fname, s->filename, fname_len);
- fname[fname_len] = '\0';
- irep->filename = fname;
- irep->own_filename = TRUE;
+ 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;
@@ -3025,9 +3225,8 @@ 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->pc0 = p->pc1 = p->pc2 = p->pc3 = JMPLINK_START;
p->prev = s->loop;
- p->ensure_level = s->ensure_level;
p->acc = cursp();
s->loop = p;
@@ -3043,7 +3242,6 @@ loop_break(codegen_scope *s, node *tree)
}
else {
struct loopinfo *loop;
- int n = 0;
if (tree) {
gen_retval(s, tree);
@@ -3052,7 +3250,6 @@ loop_break(codegen_scope *s, node *tree)
loop = s->loop;
while (loop) {
if (loop->type == LOOP_BEGIN) {
- n++;
loop = loop->prev;
}
else if (loop->type == LOOP_RESCUE) {
@@ -3066,20 +3263,14 @@ loop_break(codegen_scope *s, node *tree)
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);
+ tmp = genjmp(s, OP_JMPUW, loop->pc3);
loop->pc3 = tmp;
}
else {
@@ -3102,6 +3293,28 @@ loop_pop(codegen_scope *s, int val)
if (val) push();
}
+static int
+catch_handler_new(codegen_scope *s)
+{
+ size_t newsize = sizeof(struct mrb_irep_catch_handler) * (s->irep->clen + 1);
+ s->catch_table = (struct mrb_irep_catch_handler *)codegen_realloc(s, (void *)s->catch_table, newsize);
+ return s->irep->clen ++;
+}
+
+static void
+catch_handler_set(codegen_scope *s, int ent, enum mrb_catch_type type, uint32_t begin, uint32_t end, uint32_t target)
+{
+ struct mrb_irep_catch_handler *e;
+
+ mrb_assert(ent >= 0 && ent < s->irep->clen);
+
+ e = &s->catch_table[ent];
+ uint8_to_bin(type, &e->type);
+ mrb_irep_catch_handler_pack(begin, e->begin);
+ mrb_irep_catch_handler_pack(end, e->end);
+ mrb_irep_catch_handler_pack(target, e->target);
+}
+
static struct RProc*
generate_code(mrb_state *mrb, parser_state *p, int val)
{
@@ -3109,12 +3322,9 @@ generate_code(mrb_state *mrb, parser_state *p, int val)
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_sym = p->filename_sym;
scope->filename_index = p->current_filename_index;
MRB_TRY(&scope->jmp) {
@@ -3151,13 +3361,14 @@ mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
{
int i;
+ if (irep->flags & MRB_IREP_NO_FREE) return;
if (irep->lv) {
- mrb_free(mrb, irep->lv);
+ mrb_free(mrb, (void*)irep->lv);
irep->lv = NULL;
}
-
+ if (!irep->reps) return;
for (i = 0; i < irep->rlen; ++i) {
- mrb_irep_remove_lv(mrb, irep->reps[i]);
+ mrb_irep_remove_lv(mrb, (mrb_irep*)irep->reps[i]);
}
}
@@ -3165,21 +3376,20 @@ mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
#define Z 1
#define S 3
#define W 4
+#define OPCODE(_,x) x,
/* 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,
+#define BSS 6
#include "mruby/ops.h"
-#undef OPCODE
#undef B
#undef BB
-#undef BS
-#undef SB
#undef BBB
+#undef BS
+#undef BSS
};
/* EXT1 instruction sizes */
uint8_t mrb_insn_size1[] = {
@@ -3187,29 +3397,30 @@ uint8_t mrb_insn_size1[] = {
#define BB 4
#define BBB 5
#define BS 5
-#define SB 5
-#define OPCODE(_,x) x,
+#define BSS 7
#include "mruby/ops.h"
-#undef OPCODE
#undef B
+#undef BS
+#undef BSS
};
/* EXT2 instruction sizes */
uint8_t mrb_insn_size2[] = {
#define B 2
-#define OPCODE(_,x) x,
+#define BS 4
+#define BSS 6
#include "mruby/ops.h"
-#undef OPCODE
+#undef B
#undef BB
#undef BBB
#undef BS
-#undef SB
+#undef BSS
};
/* EXT3 instruction sizes */
+#define B 3
#define BB 5
#define BBB 6
-#define BS 4
-#define SB 5
+#define BS 5
+#define BSS 7
uint8_t mrb_insn_size3[] = {
-#define OPCODE(_,x) x,
#include "mruby/ops.h"
};
diff --git a/mrbgems/mruby-compiler/core/keywords b/mrbgems/mruby-compiler/core/keywords
index 9cb86608c..a5961b6b8 100644
--- a/mrbgems/mruby-compiler/core/keywords
+++ b/mrbgems/mruby-compiler/core/keywords
@@ -1,8 +1,10 @@
%{
+/* Workaround for `enable_cxx_exception` (#5199) */
+#if defined __cplusplus && __cplusplus >= 201103L
+# define register
+#endif
+
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;
diff --git a/mrbgems/mruby-compiler/core/lex.def b/mrbgems/mruby-compiler/core/lex.def
index 2ff266481..7c6f316f0 100644
--- a/mrbgems/mruby-compiler/core/lex.def
+++ b/mrbgems/mruby-compiler/core/lex.def
@@ -1,5 +1,5 @@
-/* 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 */
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' mrbgems/mruby-compiler/core/keywords */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
@@ -25,16 +25,18 @@
&& ('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 <[email protected]>."
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <[email protected]>."
#endif
-#line 1 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 1 "mrbgems/mruby-compiler/core/keywords"
+
+/* Workaround for `enable_cxx_exception` (#5199) */
+#if defined __cplusplus && __cplusplus >= 201103L
+# define register
+#endif
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"
+#line 10 "mrbgems/mruby-compiler/core/keywords"
struct kwtable;
#define TOTAL_KEYWORDS 40
@@ -52,7 +54,7 @@ inline
#endif
#endif
static unsigned int
-hash (const char *str, unsigned int len)
+hash (register const char *str, register size_t len)
{
static const unsigned char asso_values[] =
{
@@ -83,7 +85,7 @@ hash (const char *str, unsigned int len)
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51
};
- int hval = len;
+ register unsigned int hval = len;
switch (hval)
{
@@ -98,109 +100,103 @@ hash (const char *str, unsigned int len)
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)
+mrb_reserved_word (register const char *str, register size_t len)
{
static const struct kwtable wordlist[] =
{
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 18 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 20 "mrbgems/mruby-compiler/core/keywords"
{"break", {keyword_break, keyword_break}, EXPR_MID},
-#line 23 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 25 "mrbgems/mruby-compiler/core/keywords"
{"else", {keyword_else, keyword_else}, EXPR_BEG},
-#line 33 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 35 "mrbgems/mruby-compiler/core/keywords"
{"nil", {keyword_nil, keyword_nil}, EXPR_END},
-#line 26 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 28 "mrbgems/mruby-compiler/core/keywords"
{"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG},
-#line 25 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 27 "mrbgems/mruby-compiler/core/keywords"
{"end", {keyword_end, keyword_end}, EXPR_END},
-#line 42 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 44 "mrbgems/mruby-compiler/core/keywords"
{"then", {keyword_then, keyword_then}, EXPR_BEG},
-#line 34 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 36 "mrbgems/mruby-compiler/core/keywords"
{"not", {keyword_not, keyword_not}, EXPR_ARG},
-#line 27 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 29 "mrbgems/mruby-compiler/core/keywords"
{"false", {keyword_false, keyword_false}, EXPR_END},
-#line 40 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 42 "mrbgems/mruby-compiler/core/keywords"
{"self", {keyword_self, keyword_self}, EXPR_END},
-#line 24 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 26 "mrbgems/mruby-compiler/core/keywords"
{"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE},
-#line 37 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 39 "mrbgems/mruby-compiler/core/keywords"
{"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID},
-#line 43 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 45 "mrbgems/mruby-compiler/core/keywords"
{"true", {keyword_true, keyword_true}, EXPR_END},
-#line 46 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 48 "mrbgems/mruby-compiler/core/keywords"
{"until", {keyword_until, modifier_until}, EXPR_VALUE},
-#line 45 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 47 "mrbgems/mruby-compiler/core/keywords"
{"unless", {keyword_unless, modifier_unless}, EXPR_VALUE},
-#line 39 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 41 "mrbgems/mruby-compiler/core/keywords"
{"return", {keyword_return, keyword_return}, EXPR_MID},
-#line 21 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 23 "mrbgems/mruby-compiler/core/keywords"
{"def", {keyword_def, keyword_def}, EXPR_FNAME},
-#line 16 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 18 "mrbgems/mruby-compiler/core/keywords"
{"and", {keyword_and, keyword_and}, EXPR_VALUE},
-#line 22 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 24 "mrbgems/mruby-compiler/core/keywords"
{"do", {keyword_do, keyword_do}, EXPR_BEG},
-#line 49 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 51 "mrbgems/mruby-compiler/core/keywords"
{"yield", {keyword_yield, keyword_yield}, EXPR_ARG},
-#line 28 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 30 "mrbgems/mruby-compiler/core/keywords"
{"for", {keyword_for, keyword_for}, EXPR_VALUE},
-#line 44 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 46 "mrbgems/mruby-compiler/core/keywords"
{"undef", {keyword_undef, keyword_undef}, EXPR_FNAME},
-#line 35 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 37 "mrbgems/mruby-compiler/core/keywords"
{"or", {keyword_or, keyword_or}, EXPR_VALUE},
-#line 30 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 32 "mrbgems/mruby-compiler/core/keywords"
{"in", {keyword_in, keyword_in}, EXPR_VALUE},
-#line 47 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 49 "mrbgems/mruby-compiler/core/keywords"
{"when", {keyword_when, keyword_when}, EXPR_VALUE},
-#line 38 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 40 "mrbgems/mruby-compiler/core/keywords"
{"retry", {keyword_retry, keyword_retry}, EXPR_END},
-#line 29 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 31 "mrbgems/mruby-compiler/core/keywords"
{"if", {keyword_if, modifier_if}, EXPR_VALUE},
-#line 19 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 21 "mrbgems/mruby-compiler/core/keywords"
{"case", {keyword_case, keyword_case}, EXPR_VALUE},
-#line 36 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 38 "mrbgems/mruby-compiler/core/keywords"
{"redo", {keyword_redo, keyword_redo}, EXPR_END},
-#line 32 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 34 "mrbgems/mruby-compiler/core/keywords"
{"next", {keyword_next, keyword_next}, EXPR_MID},
-#line 41 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 43 "mrbgems/mruby-compiler/core/keywords"
{"super", {keyword_super, keyword_super}, EXPR_ARG},
-#line 31 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 33 "mrbgems/mruby-compiler/core/keywords"
{"module", {keyword_module, keyword_module}, EXPR_VALUE},
-#line 17 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 19 "mrbgems/mruby-compiler/core/keywords"
{"begin", {keyword_begin, keyword_begin}, EXPR_BEG},
-#line 12 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 14 "mrbgems/mruby-compiler/core/keywords"
{"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END},
-#line 11 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 13 "mrbgems/mruby-compiler/core/keywords"
{"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END},
-#line 10 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 12 "mrbgems/mruby-compiler/core/keywords"
{"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
-#line 14 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 16 "mrbgems/mruby-compiler/core/keywords"
{"END", {keyword_END, keyword_END}, EXPR_END},
-#line 15 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 17 "mrbgems/mruby-compiler/core/keywords"
{"alias", {keyword_alias, keyword_alias}, EXPR_FNAME},
-#line 13 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 15 "mrbgems/mruby-compiler/core/keywords"
{"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
{""},
-#line 20 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 22 "mrbgems/mruby-compiler/core/keywords"
{"class", {keyword_class, keyword_class}, EXPR_CLASS},
{""}, {""},
-#line 48 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 50 "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);
+ register unsigned int key = hash (str, len);
- if (key <= MAX_HASH_VALUE && key >= 0)
+ if (key <= MAX_HASH_VALUE)
{
- const char *s = wordlist[key].name;
+ register const char *s = wordlist[key].name;
if (*str == *s && !strcmp (str + 1, s + 1))
return &wordlist[key];
@@ -208,4 +204,4 @@ mrb_reserved_word (const char *str, unsigned int len)
}
return 0;
}
-#line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 52 "mrbgems/mruby-compiler/core/keywords"
diff --git a/mrbgems/mruby-compiler/core/node.h b/mrbgems/mruby-compiler/core/node.h
index 219bddab0..a57b7bdf7 100644
--- a/mrbgems/mruby-compiler/core/node.h
+++ b/mrbgems/mruby-compiler/core/node.h
@@ -51,6 +51,7 @@ enum node_type {
NODE_IVAR,
NODE_CONST,
NODE_CVAR,
+ NODE_NVAR,
NODE_NTH_REF,
NODE_BACK_REF,
NODE_MATCH,
diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 5e6cda236..7d9db4a2b 100644
--- a/mrbgems/mruby-compiler/core/parse.y
+++ b/mrbgems/mruby-compiler/core/parse.y
@@ -9,14 +9,7 @@
#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
+#define YYSTACK_USE_ALLOCA 1
#include <ctype.h>
#include <errno.h>
@@ -27,6 +20,9 @@
#include <mruby/proc.h>
#include <mruby/error.h>
#include <mruby/throw.h>
+#include <mruby/string.h>
+#include <mruby/dump.h>
+#include <mruby/presym.h>
#include "node.h"
#define YYLEX_PARAM p
@@ -75,6 +71,10 @@ typedef unsigned int stack_type;
#define nsym(x) ((node*)(intptr_t)(x))
#define nint(x) ((node*)(intptr_t)(x))
#define intn(x) ((int)(intptr_t)(x))
+#define typen(x) ((enum node_type)(intptr_t)(x))
+
+#define NUM_SUFFIX_R (1<<0)
+#define NUM_SUFFIX_I (1<<1)
static inline mrb_sym
intern_cstr_gen(parser_state *p, const char *s)
@@ -90,12 +90,7 @@ intern_gen(parser_state *p, const char *s, size_t 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))
+#define intern_op(op) MRB_OPSYM_2(p->mrb, op)
static void
cons_free_gen(parser_state *p, node *cons)
@@ -133,6 +128,10 @@ cons_gen(parser_state *p, node *car, node *cdr)
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))
@@ -185,12 +184,11 @@ append_gen(parser_state *p, node *a, node *b)
node *c = a;
if (!a) return b;
+ if (!b) return a;
while (c->cdr) {
c = c->cdr;
}
- if (b) {
- c->cdr = b;
- }
+ c->cdr = b;
return a;
}
#define append(a,b) append_gen(p,(a),(b))
@@ -216,6 +214,26 @@ parser_strdup(parser_state *p, const char *s)
#undef strdup
#define strdup(s) parser_strdup(p, s)
+static void
+dump_int(uint16_t i, char *s)
+{
+ char *p = s;
+ char *t = s;
+
+ while (i > 0) {
+ *p++ = (i % 10)+'0';
+ i /= 10;
+ }
+ if (p == s) *p++ = '0';
+ *p = 0;
+ p--; /* point the last char */
+ while (t < p) {
+ char c = *t;
+ *t++ = *p;
+ *p-- = c;
+ }
+}
+
/* xxx ----------------------------- */
static node*
@@ -250,6 +268,7 @@ local_unnest(parser_state *p)
static mrb_bool
local_var_p(parser_state *p, mrb_sym sym)
{
+ const struct RProc *u;
node *l = p->locals;
while (l) {
@@ -260,6 +279,21 @@ local_var_p(parser_state *p, mrb_sym sym)
}
l = l->cdr;
}
+
+ u = p->upper;
+ while (u && !MRB_PROC_CFUNC_P(u)) {
+ const struct mrb_irep *ir = u->body.irep;
+ const mrb_sym *v = ir->lv;
+ int i;
+
+ if (v) {
+ for (i=0; i+1 < ir->nlocals; i++) {
+ if (v[i] == sym) return TRUE;
+ }
+ }
+ if (MRB_PROC_SCOPE_P(u)) break;
+ u = u->upper;
+ }
return FALSE;
}
@@ -279,12 +313,44 @@ local_add(parser_state *p, mrb_sym sym)
}
}
+static void
+local_add_blk(parser_state *p, mrb_sym blk)
+{
+ /* allocate register for block */
+ local_add_f(p, blk ? blk : intern_op(and));
+}
+
+static void
+local_add_kw(parser_state *p, mrb_sym kwd)
+{
+ /* allocate register for keywords hash */
+ local_add_f(p, kwd ? kwd : intern_op(pow));
+}
+
static node*
locals_node(parser_state *p)
{
return p->locals ? p->locals->car : NULL;
}
+static void
+nvars_nest(parser_state *p)
+{
+ p->nvars = cons(nint(0), p->nvars);
+}
+
+static void
+nvars_block(parser_state *p)
+{
+ p->nvars = cons(nint(-2), p->nvars);
+}
+
+static void
+nvars_unnest(parser_state *p)
+{
+ p->nvars = p->nvars->cdr;
+}
+
/* (:scope (vars..) (prog...)) */
static node*
new_scope(parser_state *p, node *body)
@@ -619,6 +685,16 @@ new_cvar(parser_state *p, mrb_sym sym)
return cons((node*)NODE_CVAR, nsym(sym));
}
+/* (:nvar . a) */
+static node*
+new_nvar(parser_state *p, int num)
+{
+ int nvars = intn(p->nvars->car);
+
+ p->nvars->car = nint(nvars > num ? nvars : num);
+ return cons((node*)NODE_NVAR, nint(num));
+}
+
/* (:const . a) */
static node*
new_const(parser_state *p, mrb_sym sym)
@@ -660,7 +736,19 @@ new_module(parser_state *p, node *m, node *b)
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);
+ return list5((node*)NODE_DEF, nsym(m), 0, a, b);
+}
+
+static void
+defn_setup(parser_state *p, node *d, node *a, node *b)
+{
+ node *n = d->cdr->cdr;
+
+ n->car = locals_node(p);
+ p->cmdarg_stack = intn(n->cdr->car);
+ n->cdr->car = a;
+ local_resume(p, n->cdr->cdr->car);
+ n->cdr->cdr->car = b;
}
/* (:sdef obj m lv (arg . body)) */
@@ -668,7 +756,19 @@ 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);
+ return list6((node*)NODE_SDEF, o, nsym(m), 0, a, b);
+}
+
+static void
+defs_setup(parser_state *p, node *d, node *a, node *b)
+{
+ node *n = d->cdr->cdr->cdr;
+
+ n->car = locals_node(p);
+ p->cmdarg_stack = intn(n->cdr->car);
+ n->cdr->car = a;
+ local_resume(p, n->cdr->cdr->car);
+ n->cdr->cdr->car = b;
}
/* (:arg . sym) */
@@ -678,6 +778,34 @@ 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 (typen(n->car->car) == 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;
+ }
+}
+
+static void
+local_add_lv(parser_state *p, node *lv)
+{
+ while (lv) {
+ local_add_f(p, sym(lv->car));
+ lv = lv->cdr;
+ }
+}
+
/* (m o r m2 tail) */
/* m: (a b c) */
/* o: ((a . e1) (b . e2)) */
@@ -689,9 +817,17 @@ 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);
+ while (opt) {
+ /* opt: (sym . (opt . lv)) -> (sym . opt) */
+ local_add_lv(p, opt->car->cdr->cdr);
+ opt->car->cdr = opt->car->cdr->car;
+ opt = opt->cdr;
+ }
return cons(m, n);
}
@@ -701,23 +837,23 @@ 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, "**"));
+ local_add_kw(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : 0);
}
- /* allocate register for block */
- local_add_f(p, blk? blk : mrb_intern_lit(p->mrb, "&"));
+ local_add_blk(p, blk);
- // allocate register for keywords arguments
- // order is for Proc#parameters
+ /* 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
+ 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
+ if (k->car->cdr->cdr->car) { /* allocate keywords with default */
+ local_add_lv(p, k->car->cdr->cdr->car->cdr);
+ k->car->cdr->cdr->car = k->car->cdr->cdr->car->car;
local_add_f(p, sym(k->car->cdr->car));
}
}
@@ -733,6 +869,13 @@ new_kw_arg(parser_state *p, mrb_sym kw, node *def_arg)
return list3((node*)NODE_KW_ARG, nsym(kw), def_arg);
}
+/* (:kw_rest_args . a) */
+static node*
+new_kw_rest_args(parser_state *p, node *a)
+{
+ return cons((node*)NODE_KW_REST_ARGS, a);
+}
+
/* (:block_arg . a) */
static node*
new_block_arg(parser_state *p, node *a)
@@ -740,10 +883,41 @@ new_block_arg(parser_state *p, node *a)
return cons((node*)NODE_BLOCK_ARG, a);
}
+static node*
+setup_numparams(parser_state *p, node *a)
+{
+ int nvars = intn(p->nvars->car);
+ if (nvars > 0) {
+ int i;
+ mrb_sym sym;
+ // m || opt || rest || tail
+ if (a && (a->car || (a->cdr && a->cdr->car) || (a->cdr->cdr && a->cdr->cdr->car) || (a->cdr->cdr->cdr->cdr && a->cdr->cdr->cdr->cdr->car))) {
+ yyerror(p, "ordinary parameter is defined");
+ }
+ else if (p->locals) {
+ /* p->locals should not be NULL unless error happens before the point */
+ node* args = 0;
+ for (i = nvars; i > 0; i--) {
+ char buf[3];
+
+ buf[0] = '_';
+ buf[1] = i+'0';
+ buf[2] = '\0';
+ sym = intern_cstr(buf);
+ args = cons(new_arg(p, sym), args);
+ p->locals->car = cons(nsym(sym), p->locals->car);
+ }
+ a = new_args(p, args, 0, 0, 0, 0);
+ }
+ }
+ return a;
+}
+
/* (:block arg body) */
static node*
new_block(parser_state *p, node *a, node *b)
{
+ a = setup_numparams(p, a);
return list4((node*)NODE_BLOCK, locals_node(p), a, b);
}
@@ -770,6 +944,13 @@ new_masgn(parser_state *p, node *a, node *b)
return cons((node*)NODE_MASGN, cons(a, b));
}
+/* (:masgn mlhs mrhs) no check */
+static node*
+new_masgn_param(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_MASGN, cons(a, b));
+}
+
/* (:asgn lhs rhs) */
static node*
new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
@@ -778,19 +959,45 @@ new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
}
+static node*
+new_imaginary(parser_state *p, node *imaginary)
+{
+ return new_call(p, new_const(p, MRB_SYM_2(p->mrb, Kernel)), MRB_SYM_2(p->mrb, Complex), list1(list2(list3((node*)NODE_INT, (node*)strdup("0"), nint(10)), imaginary)), 1);
+}
+
+static node*
+new_rational(parser_state *p, node *rational)
+{
+ return new_call(p, new_const(p, MRB_SYM_2(p->mrb, Kernel)), MRB_SYM_2(p->mrb, Rational), list1(list1(rational)), 1);
+}
+
/* (:int . i) */
static node*
-new_int(parser_state *p, const char *s, int base)
+new_int(parser_state *p, const char *s, int base, int suffix)
{
- return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+ node* result = list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+ return result;
}
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
/* (:float . i) */
static node*
-new_float(parser_state *p, const char *s)
+new_float(parser_state *p, const char *s, int suffix)
{
- return cons((node*)NODE_FLOAT, (node*)strdup(s));
+ node* result = cons((node*)NODE_FLOAT, (node*)strdup(s));
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+ return result;
}
#endif
@@ -808,6 +1015,86 @@ new_dstr(parser_state *p, node *a)
return cons((node*)NODE_DSTR, a);
}
+static int
+string_node_p(node *n)
+{
+ return (int)(typen(n->car) == NODE_STR);
+}
+
+static node*
+composite_string_node(parser_state *p, node *a, node *b)
+{
+ size_t newlen = (size_t)a->cdr + (size_t)b->cdr;
+ char *str = (char*)mrb_pool_realloc(p->pool, a->car, (size_t)a->cdr + 1, newlen + 1);
+ memcpy(str + (size_t)a->cdr, b->car, (size_t)b->cdr);
+ str[newlen] = '\0';
+ a->car = (node*)str;
+ a->cdr = (node*)newlen;
+ cons_free(b);
+ return a;
+}
+
+static node*
+concat_string(parser_state *p, node *a, node *b)
+{
+ if (string_node_p(a)) {
+ if (string_node_p(b)) {
+ /* a == NODE_STR && b == NODE_STR */
+ composite_string_node(p, a->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+ else {
+ /* a == NODE_STR && b == NODE_DSTR */
+
+ if (string_node_p(b->cdr->car)) {
+ /* a == NODE_STR && b->[NODE_STR, ...] */
+ composite_string_node(p, a->cdr, b->cdr->car->cdr);
+ cons_free(b->cdr->car);
+ b->cdr->car = a;
+ return b;
+ }
+ }
+ }
+ else {
+ node *c; /* last node of a */
+ for (c = a; c->cdr != NULL; c = c->cdr) ;
+
+ if (string_node_p(b)) {
+ /* a == NODE_DSTR && b == NODE_STR */
+ if (string_node_p(c->car)) {
+ /* a->[..., NODE_STR] && b == NODE_STR */
+ composite_string_node(p, c->car->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+
+ push(a, b);
+ return a;
+ }
+ else {
+ /* a == NODE_DSTR && b == NODE_DSTR */
+ if (string_node_p(c->car) && string_node_p(b->cdr->car)) {
+ /* a->[..., NODE_STR] && b->[NODE_STR, ...] */
+ node *d = b->cdr;
+ cons_free(b);
+ composite_string_node(p, c->car->cdr, d->car->cdr);
+ cons_free(d->car);
+ c->cdr = d->cdr;
+ cons_free(d);
+ return a;
+ }
+ else {
+ c->cdr = b->cdr;
+ cons_free(b);
+ return a;
+ }
+ }
+ }
+
+ return new_dstr(p, list2(a, b));
+}
+
/* (:str . (s . len)) */
static node*
new_xstr(parser_state *p, const char *s, int len)
@@ -919,11 +1206,26 @@ args_with_block(parser_state *p, node *a, node *b)
}
static void
+endless_method_name(parser_state *p, node *defn)
+{
+ mrb_sym sym = sym(defn->cdr->car);
+ mrb_int len;
+ const char *name = mrb_sym_name_len(p->mrb, sym, &len);
+
+ if (len > 1 && name[len-1] == '=') {
+ for (int i=0; i<len-1; i++) {
+ if (!identchar(name[i])) return;
+ }
+ yyerror(p, "setter method cannot be defined by endless method definition");
+ }
+}
+
+static void
call_with_block(parser_state *p, node *a, node *b)
{
node *n;
- switch ((enum node_type)intn(a->car)) {
+ switch (typen(a->car)) {
case NODE_SUPER:
case NODE_ZSUPER:
if (!a->cdr) a->cdr = cons(0, b);
@@ -997,7 +1299,7 @@ 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))));
+ return cons(nint(type), cons(nint(0), cons(nint(paren), nint(term))));
}
static void
@@ -1070,11 +1372,9 @@ heredoc_end(parser_state *p)
p->parsing_heredoc = p->parsing_heredoc->cdr;
if (p->parsing_heredoc == NULL) {
p->lstate = EXPR_BEG;
- p->cmd_start = TRUE;
end_strterm(p);
p->lex_strterm = p->lex_strterm_before_heredoc;
p->lex_strterm_before_heredoc = NULL;
- p->heredoc_end_now = TRUE;
}
else {
/* next heredoc */
@@ -1087,7 +1387,8 @@ heredoc_end(parser_state *p)
%}
-%pure-parser
+%define parse.error verbose
+%define api.pure
%parse-param {parser_state *p}
%lex-param {parser_state *p}
@@ -1151,14 +1452,24 @@ heredoc_end(parser_state *p)
keyword__FILE__
keyword__ENCODING__
-%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL_TAG
-%token <nd> tINTEGER tFLOAT tCHAR tXSTRING tREGEXP
+%token <id> tIDENTIFIER "local variable or method"
+%token <id> tFID "method"
+%token <id> tGVAR "global variable"
+%token <id> tIVAR "instance variable"
+%token <id> tCONSTANT "constant"
+%token <id> tCVAR "class variable"
+%token <id> tLABEL_TAG "label"
+%token <nd> tINTEGER "integer literal"
+%token <nd> tFLOAT "float literal"
+%token <nd> tCHAR "character literal"
+%token <nd> tXSTRING tREGEXP
%token <nd> tSTRING tSTRING_PART tSTRING_MID
%token <nd> tNTH_REF tBACK_REF
%token <num> tREGEXP_END
+%token <num> tNUMPARAM "numbered parameter"
-%type <nd> singleton string string_rep string_interp xstring regexp
-%type <nd> literal numeric cpath symbol
+%type <nd> singleton string string_fragment string_rep string_interp xstring regexp
+%type <nd> literal numeric cpath symbol defn_head defs_head
%type <nd> top_compstmt top_stmts top_stmt
%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
%type <nd> expr_value arg_rhs primary_value
@@ -1168,7 +1479,8 @@ heredoc_end(parser_state *p)
%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs
%type <nd> command_asgn command_rhs mrhs superclass block_call block_command
%type <nd> f_block_optarg f_block_opt
-%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
+%type <nd> f_opt_arglist_paren f_arglist_paren f_arglist
+%type <nd> f_args f_arg f_arg_item f_optarg f_margs
%type <nd> assoc_list assocs assoc undef_list backref for_var
%type <nd> block_param opt_block_param block_param_def f_opt
%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body
@@ -1183,38 +1495,41 @@ heredoc_end(parser_state *p)
%type <nd> f_block_kwarg f_block_kw block_args_tail opt_block_args_tail
%type <id> 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 tUPLUS "unary plus"
+%token tUMINUS "unary minus"
+%token tCMP "<=>"
+%token tEQ "=="
+%token tEQQ "==="
+%token tNEQ "!="
+%token tGEQ ">="
+%token tLEQ "<="
+%token tANDOP "&&"
+%token tOROP "||"
+%token tMATCH "=~"
+%token tNMATCH "!~"
+%token tDOT2 ".."
+%token tDOT3 "..."
+%token tBDOT2 tBDOT3 /* (.. and (... */
%token tAREF tASET /* [] and []= */
-%token tLSHFT tRSHFT /* << and >> */
-%token tCOLON2 /* :: */
+%token tLSHFT "<<"
+%token tRSHFT ">>"
+%token tCOLON2 "::"
%token tCOLON3 /* :: at EXPR_BEG */
%token <id> 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 <nd> tHEREDOC_BEG /* <<, <<- */
+%token tASSOC "=>"
+%token tLPAREN tLPAREN_ARG "("
+%token tRPAREN ")"
+%token tLBRACK "["
+%token tLBRACE tLBRACE_ARG "{"
+%token tSTAR "*"
+%token tPOW tDSTAR "**"
+%token tAMPER "&"
+%token tLAMBDA "->"
+%token tANDDOT "&."
+%token tSYMBEG "symbol"
+%token tSTRING_BEG "string literal"
+%token tXSTRING_BEG tSTRING_DVAR tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG tLAMBEG
+%token <nd> tHEREDOC_BEG "here document"
%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM
%token <nd> tHD_STRING_PART tHD_STRING_MID
@@ -1231,7 +1546,7 @@ heredoc_end(parser_state *p)
%right '=' tOP_ASGN
%left modifier_rescue
%right '?' ':' tLABEL_TAG
-%nonassoc tDOT2 tDOT3
+%nonassoc tDOT2 tDOT3 tBDOT2 tBDOT3
%left tOROP
%left tANDOP
%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
@@ -1288,11 +1603,13 @@ top_stmt : stmt
| keyword_BEGIN
{
$<nd>$ = local_switch(p);
+ nvars_block(p);
}
'{' top_compstmt '}'
{
yyerror(p, "BEGIN not supported");
local_resume(p, $<nd>2);
+ nvars_unnest(p);
$$ = 0;
}
;
@@ -1399,6 +1716,13 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
{
$$ = new_masgn(p, $1, new_array(p, $3));
}
+ | arg tASSOC tIDENTIFIER
+ {
+ node *lhs = new_lvar(p, $3);
+ void_expr_error(p, $1);
+ assignable(p, lhs);
+ $$ = new_asgn(p, lhs, $1);
+ }
| expr
;
@@ -1410,9 +1734,9 @@ command_asgn : lhs '=' command_rhs
{
$$ = new_op_asgn(p, $1, $2, $3);
}
- | primary_value '[' opt_call_args rbracket tOP_ASGN command_rhs
+ | primary_value '[' opt_call_args ']' tOP_ASGN command_rhs
{
- $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
+ $$ = new_op_asgn(p, new_call(p, $1, intern_op(aref), $3, '.'), $5, $6);
}
| primary_value call_op tIDENTIFIER tOP_ASGN command_rhs
{
@@ -1431,6 +1755,44 @@ command_asgn : lhs '=' command_rhs
{
$$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5);
}
+ | defn_head f_opt_arglist_paren '=' command
+ {
+ $$ = $1;
+ endless_method_name(p, $1);
+ void_expr_error(p, $4);
+ defn_setup(p, $$, $2, $4);
+ nvars_unnest(p);
+ p->in_def--;
+ }
+ | defn_head f_opt_arglist_paren '=' command modifier_rescue arg
+ {
+ $$ = $1;
+ endless_method_name(p, $1);
+ void_expr_error(p, $4);
+ void_expr_error(p, $6);
+ defn_setup(p, $$, $2, new_mod_rescue(p, $4, $6));
+ nvars_unnest(p);
+ p->in_def--;
+ }
+ | defs_head f_opt_arglist_paren '=' command
+ {
+ $$ = $1;
+ void_expr_error(p, $4);
+ defs_setup(p, $$, $2, $4);
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
+ | defs_head f_opt_arglist_paren '=' command modifier_rescue arg
+ {
+ $$ = $1;
+ void_expr_error(p, $4);
+ void_expr_error(p, $6);
+ defs_setup(p, $$, $2, new_mod_rescue(p, $4, $6));
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
| backref tOP_ASGN command_rhs
{
backref_error(p, $1);
@@ -1467,6 +1829,31 @@ expr : command_call
| arg
;
+
+defn_head : keyword_def fname
+ {
+ $$ = new_def(p, $2, nint(p->cmdarg_stack), local_switch(p));
+ p->cmdarg_stack = 0;
+ p->in_def++;
+ nvars_block(p);
+ }
+ ;
+
+defs_head : keyword_def singleton dot_or_colon
+ {
+ p->lstate = EXPR_FNAME;
+ }
+ fname
+ {
+ $$ = new_sdef(p, $2, $5, nint(p->cmdarg_stack), local_switch(p));
+ p->cmdarg_stack = 0;
+ p->in_def++;
+ p->in_single++;
+ nvars_block(p);
+ p->lstate = EXPR_ENDFN; /* force for args */
+ }
+ ;
+
expr_value : expr
{
if (!$1) $$ = new_nil(p);
@@ -1490,6 +1877,7 @@ block_command : block_call
cmd_brace_block : tLBRACE_ARG
{
local_nest(p);
+ nvars_nest(p);
}
opt_block_param
compstmt
@@ -1497,6 +1885,7 @@ cmd_brace_block : tLBRACE_ARG
{
$$ = new_block(p, $3, $4);
local_unnest(p);
+ nvars_unnest(p);
}
;
@@ -1639,9 +2028,9 @@ mlhs_node : variable
{
assignable(p, $1);
}
- | primary_value '[' opt_call_args rbracket
+ | primary_value '[' opt_call_args ']'
{
- $$ = new_call(p, $1, intern("[]",2), $3, '.');
+ $$ = new_call(p, $1, intern_op(aref), $3, '.');
}
| primary_value call_op tIDENTIFIER
{
@@ -1678,9 +2067,9 @@ lhs : variable
{
assignable(p, $1);
}
- | primary_value '[' opt_call_args rbracket
+ | primary_value '[' opt_call_args ']'
{
- $$ = new_call(p, $1, intern("[]",2), $3, '.');
+ $$ = new_call(p, $1, intern_op(aref), $3, '.');
}
| primary_value call_op tIDENTIFIER
{
@@ -1711,6 +2100,10 @@ lhs : variable
backref_error(p, $1);
$$ = 0;
}
+ | tNUMPARAM
+ {
+ yyerror(p, "can't assign to numbered parameter");
+ }
;
cname : tIDENTIFIER
@@ -1722,11 +2115,11 @@ cname : tIDENTIFIER
cpath : tCOLON3 cname
{
- $$ = cons((node*)1, nsym($2));
+ $$ = cons(nint(1), nsym($2));
}
| cname
{
- $$ = cons((node*)0, nsym($1));
+ $$ = cons(nint(0), nsym($1));
}
| primary_value tCOLON2 cname
{
@@ -1764,36 +2157,36 @@ undef_list : fsym
}
;
-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('`'); }
+op : '|' { $$ = intern_op(or); }
+ | '^' { $$ = intern_op(xor); }
+ | '&' { $$ = intern_op(and); }
+ | tCMP { $$ = intern_op(cmp); }
+ | tEQ { $$ = intern_op(eq); }
+ | tEQQ { $$ = intern_op(eqq); }
+ | tMATCH { $$ = intern_op(match); }
+ | tNMATCH { $$ = intern_op(nmatch); }
+ | '>' { $$ = intern_op(gt); }
+ | tGEQ { $$ = intern_op(ge); }
+ | '<' { $$ = intern_op(lt); }
+ | tLEQ { $$ = intern_op(le); }
+ | tNEQ { $$ = intern_op(neq); }
+ | tLSHFT { $$ = intern_op(lshift); }
+ | tRSHFT { $$ = intern_op(rshift); }
+ | '+' { $$ = intern_op(add); }
+ | '-' { $$ = intern_op(sub); }
+ | '*' { $$ = intern_op(mul); }
+ | tSTAR { $$ = intern_op(mul); }
+ | '/' { $$ = intern_op(div); }
+ | '%' { $$ = intern_op(mod); }
+ | tPOW { $$ = intern_op(pow); }
+ | tDSTAR { $$ = intern_op(pow); }
+ | '!' { $$ = intern_op(not); }
+ | '~' { $$ = intern_op(neg); }
+ | tUPLUS { $$ = intern_op(plus); }
+ | tUMINUS { $$ = intern_op(minus); }
+ | tAREF { $$ = intern_op(aref); }
+ | tASET { $$ = intern_op(aset); }
+ | '`' { $$ = intern_op(tick); }
;
reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
@@ -1818,9 +2211,9 @@ arg : lhs '=' arg_rhs
{
$$ = new_op_asgn(p, $1, $2, $3);
}
- | primary_value '[' opt_call_args rbracket tOP_ASGN arg_rhs
+ | primary_value '[' opt_call_args ']' tOP_ASGN arg_rhs
{
- $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
+ $$ = new_op_asgn(p, new_call(p, $1, intern_op(aref), $3, '.'), $5, $6);
}
| primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs
{
@@ -1853,10 +2246,26 @@ arg : lhs '=' arg_rhs
{
$$ = new_dot2(p, $1, $3);
}
+ | arg tDOT2
+ {
+ $$ = new_dot2(p, $1, new_nil(p));
+ }
+ | tBDOT2 arg
+ {
+ $$ = new_dot2(p, new_nil(p), $2);
+ }
| arg tDOT3 arg
{
$$ = new_dot3(p, $1, $3);
}
+ | arg tDOT3
+ {
+ $$ = new_dot3(p, $1, new_nil(p));
+ }
+ | tBDOT3 arg
+ {
+ $$ = new_dot3(p, new_nil(p), $2);
+ }
| arg '+' arg
{
$$ = call_bin_op(p, $1, "+", $3);
@@ -1981,6 +2390,44 @@ arg : lhs '=' arg_rhs
{
$$ = new_if(p, cond($1), $3, $6);
}
+ | defn_head f_opt_arglist_paren '=' arg
+ {
+ $$ = $1;
+ endless_method_name(p, $1);
+ void_expr_error(p, $4);
+ defn_setup(p, $$, $2, $4);
+ nvars_unnest(p);
+ p->in_def--;
+ }
+ | defn_head f_opt_arglist_paren '=' arg modifier_rescue arg
+ {
+ $$ = $1;
+ endless_method_name(p, $1);
+ void_expr_error(p, $4);
+ void_expr_error(p, $6);
+ defn_setup(p, $$, $2, new_mod_rescue(p, $4, $6));
+ nvars_unnest(p);
+ p->in_def--;
+ }
+ | defs_head f_opt_arglist_paren '=' arg
+ {
+ $$ = $1;
+ void_expr_error(p, $4);
+ defs_setup(p, $$, $2, $4);
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
+ | defs_head f_opt_arglist_paren '=' arg modifier_rescue arg
+ {
+ $$ = $1;
+ void_expr_error(p, $4);
+ void_expr_error(p, $6);
+ defs_setup(p, $$, $2, new_mod_rescue(p, $4, $6));
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
| primary
{
$$ = $1;
@@ -2016,10 +2463,50 @@ arg_rhs : arg %prec tOP_ASGN
}
;
-paren_args : '(' opt_call_args rparen
+paren_args : '(' opt_call_args ')'
{
$$ = $2;
}
+ | '(' args comma tBDOT3 rparen
+ {
+#if 1
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ $$ = cons(push($2, new_splat(p, new_lvar(p, r))),
+ new_block_arg(p, new_lvar(p, b)));
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ $$ = cons(list2(push($2, new_splat(p, new_lvar(p, r))),
+ new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k))))),
+ new_block_arg(p, new_lvar(p, b)));
+#endif
+ }
+ | '(' tBDOT3 rparen
+ {
+#if 1
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ if (local_var_p(p, r) && local_var_p(p, b)) {
+ $$ = cons(list1(new_splat(p, new_lvar(p, r))),
+ new_block_arg(p, new_lvar(p, b)));
+ }
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ if (local_var_p(p, r) && local_var_p(p, k) && local_var_p(p, b)) {
+ $$ = cons(list2(new_splat(p, new_lvar(p, r)),
+ new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k))))),
+ new_block_arg(p, new_lvar(p, b)));
+ }
+#endif
+ else {
+ yyerror(p, "unexpected argument forwarding ...");
+ $$ = 0;
+ }
+ }
;
opt_paren_args : none
@@ -2027,18 +2514,18 @@ opt_paren_args : none
;
opt_call_args : none
- | call_args
- | args ','
+ | call_args opt_terms
+ | args comma
{
$$ = cons($1,0);
NODE_LINENO($$, $1);
}
- | args comma assocs ','
+ | args comma assocs comma
{
$$ = cons(push($1, new_kw_hash(p, $3)), 0);
NODE_LINENO($$, $1);
}
- | assocs ','
+ | assocs comma
{
$$ = cons(list1(new_kw_hash(p, $1)), 0);
NODE_LINENO($$, $1);
@@ -2101,7 +2588,7 @@ opt_block_arg : comma block_arg
;
comma : ','
- | ',' heredoc_bodies
+ | ',' opt_nl heredoc_bodies
;
args : arg
@@ -2152,6 +2639,10 @@ primary : literal
| heredoc
| var_ref
| backref
+ | tNUMPARAM
+ {
+ $$ = new_nvar(p, $1);
+ }
| tFID
{
$$ = new_fcall(p, $1, 0);
@@ -2304,6 +2795,7 @@ primary : literal
if (p->in_def || p->in_single)
yyerror(p, "class definition in method body");
$<nd>$ = local_switch(p);
+ nvars_block(p);
}
bodystmt
keyword_end
@@ -2311,6 +2803,7 @@ primary : literal
$$ = new_class(p, $2, $3, $5);
SET_LINENO($$, $1);
local_resume(p, $<nd>4);
+ nvars_unnest(p);
}
| keyword_class
tLSHFT expr
@@ -2321,6 +2814,7 @@ primary : literal
term
{
$<nd>$ = cons(local_switch(p), nint(p->in_single));
+ nvars_block(p);
p->in_single = 0;
}
bodystmt
@@ -2329,6 +2823,7 @@ primary : literal
$$ = new_sclass(p, $3, $7);
SET_LINENO($$, $1);
local_resume(p, $<nd>6->car);
+ nvars_unnest(p);
p->in_def = $<num>4;
p->in_single = intn($<nd>6->cdr);
}
@@ -2338,6 +2833,7 @@ primary : literal
if (p->in_def || p->in_single)
yyerror(p, "module definition in method body");
$<nd>$ = local_switch(p);
+ nvars_block(p);
}
bodystmt
keyword_end
@@ -2345,47 +2841,28 @@ primary : literal
$$ = new_module(p, $2, $4);
SET_LINENO($$, $1);
local_resume(p, $<nd>3);
+ nvars_unnest(p);
}
- | keyword_def fname
- {
- $<stack>$ = p->cmdarg_stack;
- p->cmdarg_stack = 0;
- }
- {
- p->in_def++;
- $<nd>$ = local_switch(p);
- }
+ | defn_head
f_arglist
bodystmt
keyword_end
{
- $$ = new_def(p, $2, $5, $6);
- SET_LINENO($$, $1);
- local_resume(p, $<nd>4);
+ $$ = $1;
+ defn_setup(p, $$, $2, $3);
+ nvars_unnest(p);
p->in_def--;
- p->cmdarg_stack = $<stack>3;
- }
- | keyword_def singleton dot_or_colon
- {
- p->lstate = EXPR_FNAME;
- $<stack>$ = p->cmdarg_stack;
- p->cmdarg_stack = 0;
- }
- fname
- {
- p->in_single++;
- p->lstate = EXPR_ENDFN; /* force for args */
- $<nd>$ = local_switch(p);
}
+ | defs_head
f_arglist
bodystmt
keyword_end
{
- $$ = new_sdef(p, $2, $5, $7, $8);
- SET_LINENO($$, $1);
- local_resume(p, $<nd>6);
+ $$ = $1;
+ defs_setup(p, $$, $2, $3);
+ nvars_unnest(p);
+ p->in_def--;
p->in_single--;
- p->cmdarg_stack = $<stack>4;
}
| keyword_break
{
@@ -2444,61 +2921,47 @@ for_var : lhs
| mlhs
;
-f_marg : f_norm_arg
- {
- $$ = new_arg(p, $1);
- }
- | tLPAREN f_margs rparen
- {
- $$ = new_masgn(p, $2, 0);
- }
- ;
-
-f_marg_list : f_marg
- {
- $$ = list1($1);
- }
- | f_marg_list ',' f_marg
- {
- $$ = push($1, $3);
- }
- ;
-
-f_margs : f_marg_list
+f_margs : f_arg
{
$$ = list3($1,0,0);
}
- | f_marg_list ',' tSTAR f_norm_arg
+ | f_arg ',' tSTAR f_norm_arg
{
$$ = list3($1, new_arg(p, $4), 0);
}
- | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
+ | f_arg ',' tSTAR f_norm_arg ',' f_arg
{
$$ = list3($1, new_arg(p, $4), $6);
}
- | f_marg_list ',' tSTAR
+ | f_arg ',' tSTAR
{
- $$ = list3($1, (node*)-1, 0);
+ local_add_f(p, 0);
+ $$ = list3($1, nint(-1), 0);
}
- | f_marg_list ',' tSTAR ',' f_marg_list
+ | f_arg ',' tSTAR ',' f_arg
{
- $$ = list3($1, (node*)-1, $5);
+ $$ = list3($1, nint(-1), $5);
}
| tSTAR f_norm_arg
{
$$ = list3(0, new_arg(p, $2), 0);
}
- | tSTAR f_norm_arg ',' f_marg_list
+ | tSTAR f_norm_arg ',' f_arg
{
$$ = list3(0, new_arg(p, $2), $4);
}
| tSTAR
{
- $$ = list3(0, (node*)-1, 0);
+ local_add_f(p, 0);
+ $$ = list3(0, nint(-1), 0);
}
- | tSTAR ',' f_marg_list
+ | tSTAR ','
{
- $$ = list3(0, (node*)-1, $3);
+ local_add_f(p, 0);
+ }
+ f_arg
+ {
+ $$ = list3(0, nint(-1), $4);
}
;
@@ -2550,9 +3013,9 @@ block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail
{
$$ = new_args(p, $1, 0, $3, 0, $4);
}
- | f_arg ','
+ | f_arg ',' opt_block_args_tail
{
- $$ = new_args(p, $1, 0, 0, 0, 0);
+ $$ = new_args(p, $1, 0, 0, 0, $3);
}
| f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
@@ -2593,19 +3056,24 @@ block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail
;
opt_block_param : none
- | block_param_def
{
+ local_add_blk(p, 0);
+ $$ = 0;
+ }
+ | block_param_def
+ {
p->cmd_start = TRUE;
$$ = $1;
}
;
-block_param_def : '|' opt_bv_decl '|'
+block_param_def : '|' {local_add_blk(p, 0);} opt_bv_decl '|'
{
$$ = 0;
}
| tOROP
{
+ local_add_blk(p, 0);
$$ = 0;
}
| '|' block_param opt_bv_decl '|'
@@ -2660,6 +3128,7 @@ lambda_body : tLAMBEG compstmt '}'
do_block : keyword_do_block
{
local_nest(p);
+ nvars_nest(p);
}
opt_block_param
bodystmt
@@ -2667,12 +3136,13 @@ do_block : keyword_do_block
{
$$ = new_block(p,$3,$4);
local_unnest(p);
+ nvars_unnest(p);
}
;
block_call : command do_block
{
- if ($1->car == (node*)NODE_YIELD) {
+ if (typen($1->car) == NODE_YIELD) {
yyerror(p, "block given to yield");
}
else {
@@ -2714,11 +3184,11 @@ method_call : operation paren_args
}
| primary_value call_op paren_args
{
- $$ = new_call(p, $1, intern("call",4), $3, $2);
+ $$ = new_call(p, $1, MRB_SYM_2(p->mrb, call), $3, $2);
}
| primary_value tCOLON2 paren_args
{
- $$ = new_call(p, $1, intern("call",4), $3, tCOLON2);
+ $$ = new_call(p, $1, MRB_SYM_2(p->mrb, call), $3, tCOLON2);
}
| keyword_super paren_args
{
@@ -2728,15 +3198,16 @@ method_call : operation paren_args
{
$$ = new_zsuper(p);
}
- | primary_value '[' opt_call_args rbracket
+ | primary_value '[' opt_call_args ']'
{
- $$ = new_call(p, $1, intern("[]",2), $3, '.');
+ $$ = new_call(p, $1, intern_op(aref), $3, '.');
}
;
brace_block : '{'
{
local_nest(p);
+ nvars_nest(p);
$<num>$ = p->lineno;
}
opt_block_param
@@ -2745,10 +3216,12 @@ brace_block : '{'
$$ = new_block(p,$3,$4);
SET_LINENO($$, $<num>2);
local_unnest(p);
+ nvars_unnest(p);
}
| keyword_do
{
local_nest(p);
+ nvars_nest(p);
$<num>$ = p->lineno;
}
opt_block_param
@@ -2757,6 +3230,7 @@ brace_block : '{'
$$ = new_block(p,$3,$4);
SET_LINENO($$, $<num>2);
local_unnest(p);
+ nvars_unnest(p);
}
;
@@ -2818,7 +3292,14 @@ literal : numeric
| symbols
;
-string : tCHAR
+string : string_fragment
+ | string string_fragment
+ {
+ $$ = concat_string(p, $1, $2);
+ }
+ ;
+
+string_fragment : tCHAR
| tSTRING
| tSTRING_BEG tSTRING
{
@@ -3016,6 +3497,10 @@ var_lhs : variable
{
assignable(p, $1);
}
+ | tNUMPARAM
+ {
+ yyerror(p, "can't assign to numbered parameter");
+ }
;
var_ref : variable
@@ -3040,7 +3525,7 @@ var_ref : variable
}
| keyword__FILE__
{
- const char *fn = p->filename;
+ const char *fn = mrb_sym_name_len(p->mrb, p->filename_sym, NULL);
if (!fn) {
fn = "(null)";
}
@@ -3050,8 +3535,8 @@ var_ref : variable
{
char buf[16];
- snprintf(buf, sizeof(buf), "%d", p->lineno);
- $$ = new_int(p, buf, 10);
+ dump_int(p->lineno, buf);
+ $$ = new_int(p, buf, 10, 0);
}
| keyword__ENCODING__
{
@@ -3088,12 +3573,56 @@ superclass : /* term */
} */
;
-f_arglist : '(' f_args rparen
+f_opt_arglist_paren
+ : f_arglist_paren
+ | none
+ ;
+
+f_arglist_paren : '(' f_args rparen
{
$$ = $2;
p->lstate = EXPR_BEG;
p->cmd_start = TRUE;
}
+ | '(' f_arg ',' tBDOT3 rparen
+ {
+#if 1
+ /* til real keyword args implemented */
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r);
+ $$ = new_args(p, $2, 0, r, 0,
+ new_args_tail(p, 0, 0, b));
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r); local_add_f(p, k);
+ $$ = new_args(p, $2, 0, r, 0,
+ new_args_tail(p, 0, new_kw_rest_args(p, nsym(k)), b));
+#endif
+ }
+ | '(' tBDOT3 rparen
+ {
+#if 1
+ /* til real keyword args implemented */
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r);
+ $$ = new_args(p, 0, 0, r, 0,
+ new_args_tail(p, 0, 0, b));
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r); local_add_f(p, k);
+ $$ = new_args(p, 0, 0, r, 0,
+ new_args_tail(p, 0, new_kw_rest_args(p, nsym(k)), b));
+#endif
+ }
+ ;
+
+f_arglist : f_arglist_paren
| f_args term
{
$$ = $1;
@@ -3101,26 +3630,33 @@ f_arglist : '(' f_args rparen
;
f_label : tIDENTIFIER tLABEL_TAG
+ {
+ local_nest(p);
+ }
;
f_kw : f_label arg
{
void_expr_error(p, $2);
- $$ = new_kw_arg(p, $1, $2);
+ $$ = new_kw_arg(p, $1, cons($2, locals_node(p)));
+ local_unnest(p);
}
| f_label
{
$$ = new_kw_arg(p, $1, 0);
+ local_unnest(p);
}
;
f_block_kw : f_label primary_value
{
- $$ = new_kw_arg(p, $1, $2);
+ $$ = new_kw_arg(p, $1, cons($2, locals_node(p)));
+ local_unnest(p);
}
| f_label
{
$$ = new_kw_arg(p, $1, 0);
+ local_unnest(p);
}
;
@@ -3150,11 +3686,11 @@ kwrest_mark : tPOW
f_kwrest : kwrest_mark tIDENTIFIER
{
- $$ = cons((node*)NODE_KW_REST_ARGS, nsym($2));
+ $$ = new_kw_rest_args(p, nsym($2));
}
| kwrest_mark
{
- $$ = cons((node*)NODE_KW_REST_ARGS, 0);
+ $$ = new_kw_rest_args(p, 0);
}
;
@@ -3244,7 +3780,7 @@ f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
}
| /* none */
{
- local_add_f(p, mrb_intern_lit(p->mrb, "&"));
+ local_add_f(p, intern_op(and));
$$ = new_args(p, 0, 0, 0, 0, 0);
}
;
@@ -3269,6 +3805,11 @@ f_bad_arg : tCONSTANT
yyerror(p, "formal argument cannot be a class variable");
$$ = 0;
}
+ | tNUMPARAM
+ {
+ yyerror(p, "formal argument cannot be a numbered parameter");
+ $$ = 0;
+ }
;
f_norm_arg : f_bad_arg
@@ -3286,9 +3827,15 @@ f_arg_item : f_norm_arg
{
$$ = new_arg(p, $1);
}
- | tLPAREN f_margs rparen
+ | tLPAREN
{
- $$ = new_masgn(p, $2, 0);
+ $<nd>$ = local_switch(p);
+ }
+ f_margs rparen
+ {
+ $$ = new_masgn_param(p, $3, p->locals->car);
+ local_resume(p, $<nd>2);
+ local_add_f(p, 0);
}
;
@@ -3305,6 +3852,7 @@ f_arg : f_arg_item
f_opt_asgn : tIDENTIFIER '='
{
local_add_f(p, $1);
+ local_nest(p);
$$ = $1;
}
;
@@ -3312,14 +3860,16 @@ f_opt_asgn : tIDENTIFIER '='
f_opt : f_opt_asgn arg
{
void_expr_error(p, $2);
- $$ = cons(nsym($1), $2);
+ $$ = cons(nsym($1), cons($2, locals_node(p)));
+ local_unnest(p);
}
;
f_block_opt : f_opt_asgn primary_value
{
void_expr_error(p, $2);
- $$ = cons(nsym($1), $2);
+ $$ = cons(nsym($1), cons($2, locals_node(p)));
+ local_unnest(p);
}
;
@@ -3354,7 +3904,7 @@ f_rest_arg : restarg_mark tIDENTIFIER
}
| restarg_mark
{
- local_add_f(p, mrb_intern_lit(p->mrb, "*"));
+ local_add_f(p, intern_op(mul));
$$ = -1;
}
;
@@ -3390,7 +3940,7 @@ singleton : var_ref
yyerror(p, "can't define singleton method for ().");
}
else {
- switch ((enum node_type)intn($3->car)) {
+ switch (typen($3->car)) {
case NODE_STR:
case NODE_DSTR:
case NODE_XSTR:
@@ -3421,27 +3971,31 @@ assocs : assoc
$$ = list1($1);
NODE_LINENO($$, $1);
}
- | assocs ',' assoc
+ | assocs comma assoc
{
$$ = push($1, $3);
}
;
+label_tag : tLABEL_TAG
+ | tLABEL_TAG heredoc_bodies
+ ;
+
assoc : arg tASSOC arg
{
void_expr_error(p, $1);
void_expr_error(p, $3);
$$ = cons($1, $3);
}
- | tIDENTIFIER tLABEL_TAG arg
+ | tIDENTIFIER label_tag arg
{
void_expr_error(p, $3);
$$ = cons(new_sym(p, $1), $3);
}
- | string tLABEL_TAG arg
+ | string_fragment label_tag arg
{
void_expr_error(p, $3);
- if ($1->car == (node*)NODE_DSTR) {
+ if (typen($1->car) == NODE_DSTR) {
$$ = cons(new_dsym(p, $1), $3);
}
else {
@@ -3451,7 +4005,7 @@ assoc : arg tASSOC arg
| tDSTAR arg
{
void_expr_error(p, $2);
- $$ = cons(cons((node*)NODE_KW_REST_ARGS, 0), $2);
+ $$ = cons(new_kw_rest_args(p, 0), $2);
}
;
@@ -3500,14 +4054,11 @@ opt_nl : /* none */
| nl
;
-rparen : opt_nl ')'
- ;
-
-rbracket : opt_nl ']'
+rparen : opt_terms ')'
;
trailer : /* none */
- | nl
+ | terms
| comma
;
@@ -3518,7 +4069,7 @@ term : ';' {yyerrok;}
nl : '\n'
{
- p->lineno++;
+ p->lineno += $<num>1;
p->column = 0;
}
;
@@ -3542,9 +4093,10 @@ yyerror(parser_state *p, const char *s)
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);
+#ifndef MRB_NO_STDIO
+ if (p->filename_sym) {
+ const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: %s\n", filename, p->lineno, p->column, s);
}
else {
fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
@@ -3563,11 +4115,13 @@ yyerror(parser_state *p, const char *s)
}
static void
-yyerror_i(parser_state *p, const char *fmt, int i)
+yyerror_c(parser_state *p, const char *msg, char c)
{
char buf[256];
- snprintf(buf, sizeof(buf), fmt, i);
+ strncpy(buf, msg, sizeof(buf) - 2);
+ buf[sizeof(buf) - 2] = '\0';
+ strncat(buf, &c, 1);
yyerror(p, buf);
}
@@ -3578,12 +4132,13 @@ yywarn(parser_state *p, const char *s)
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);
+#ifndef MRB_NO_STDIO
+ if (p->filename_sym) {
+ const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: warning: %s\n", filename, p->lineno, p->column, s);
}
else {
- fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
+ fprintf(stderr, "line %d:%d: warning: %s\n", p->lineno, p->column, s);
}
#endif
}
@@ -3605,11 +4160,14 @@ yywarning(parser_state *p, const char *s)
}
static void
-yywarning_s(parser_state *p, const char *fmt, const char *s)
+yywarning_s(parser_state *p, const char *msg, const char *s)
{
char buf[256];
- snprintf(buf, sizeof(buf), fmt, s);
+ strncpy(buf, msg, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, s, sizeof(buf) - strlen(buf) - 1);
yywarning(p, buf);
}
@@ -3621,13 +4179,13 @@ backref_error(parser_state *p, node *n)
c = intn(n->car);
if (c == NODE_NTH_REF) {
- yyerror_i(p, "can't set variable $%" MRB_PRId, intn(n->cdr));
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr)+'0');
}
else if (c == NODE_BACK_REF) {
- yyerror_i(p, "can't set variable $%c", intn(n->cdr));
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr));
}
else {
- mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
+ mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %d", c);
}
}
@@ -3648,8 +4206,10 @@ void_expr_error(parser_state *p, node *n)
break;
case NODE_AND:
case NODE_OR:
- void_expr_error(p, n->cdr->car);
- void_expr_error(p, n->cdr->cdr);
+ if (n->cdr) {
+ void_expr_error(p, n->cdr->car);
+ void_expr_error(p, n->cdr->cdr);
+ }
break;
case NODE_BEGIN:
if (n->cdr) {
@@ -3669,6 +4229,27 @@ static mrb_bool peeks(parser_state *p, const char *s);
static mrb_bool skips(parser_state *p, const char *s);
static inline int
+nextc0(parser_state *p)
+{
+ int c;
+
+ if (p->s && p->s < p->send) {
+ c = (unsigned char)*p->s++;
+ }
+ else {
+#ifndef MRB_NO_STDIO
+ if (p->f) {
+ c = fgetc(p->f);
+ if (feof(p->f)) return -1;
+ }
+ else
+#endif
+ return -1;
+ }
+ return c;
+}
+
+static inline int
nextc(parser_state *p)
{
int c;
@@ -3682,31 +4263,18 @@ nextc(parser_state *p)
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++;
- }
+ c = nextc0(p);
+ if (c < 0) goto eof;
}
if (c >= 0) {
p->column++;
}
if (c == '\r') {
- c = nextc(p);
- if (c != '\n') {
- pushback(p, c);
- return '\r';
+ const int lf = nextc0(p);
+ if (lf == '\n') {
+ return '\n';
}
- return c;
+ if (lf > 0) pushback(p, lf);
}
return c;
@@ -3753,7 +4321,7 @@ peekc_n(parser_state *p, int n)
list = push(list, nint(c0));
} while(n--);
if (p->pb) {
- p->pb = append((node*)list, p->pb);
+ p->pb = append(list, p->pb);
}
else {
p->pb = list;
@@ -3773,7 +4341,7 @@ peeks(parser_state *p, const char *s)
{
size_t len = strlen(s);
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
if (p->f) {
int n = 0;
while (*s) {
@@ -4131,6 +4699,89 @@ read_escape(parser_state *p)
}
}
+static void
+heredoc_count_indent(parser_heredoc_info *hinf, const char *str, size_t len, size_t spaces, size_t *offset)
+{
+ size_t indent = 0;
+ *offset = 0;
+ for (size_t i = 0; i < len; i++) {
+ size_t size;
+ if (str[i] == '\n')
+ break;
+ else if (str[i] == '\t')
+ size = 8;
+ else if (ISSPACE(str[i]))
+ size = 1;
+ else
+ break;
+ size_t nindent = indent + size;
+ if (nindent > spaces || nindent > hinf->indent)
+ break;
+ indent = nindent;
+ *offset += 1;
+ }
+}
+
+static void
+heredoc_remove_indent(parser_state *p, parser_heredoc_info *hinf)
+{
+ if (!hinf->remove_indent || hinf->indent == 0)
+ return;
+ node *indented, *n, *pair, *escaped, *nspaces;
+ const char *str;
+ size_t len, spaces, offset, start, end;
+ indented = hinf->indented;
+ while (indented) {
+ n = indented->car;
+ pair = n->car;
+ str = (char*)pair->car;
+ len = (size_t)pair->cdr;
+ escaped = n->cdr->car;
+ nspaces = n->cdr->cdr;
+ if (escaped) {
+ char *newstr = strndup(str, len);
+ size_t newlen = 0;
+ start = 0;
+ while (start < len) {
+ end = escaped ? (size_t)escaped->car : len;
+ if (end > len) end = len;
+ spaces = (size_t)nspaces->car;
+ size_t esclen = end - start;
+ heredoc_count_indent(hinf, str + start, esclen, spaces, &offset);
+ esclen -= offset;
+ memcpy(newstr + newlen, str + start + offset, esclen);
+ newlen += esclen;
+ start = end;
+ if (escaped)
+ escaped = escaped->cdr;
+ nspaces = nspaces->cdr;
+ }
+ if (newlen < len)
+ newstr[newlen] = '\0';
+ pair->car = (node*)newstr;
+ pair->cdr = (node*)newlen;
+ } else {
+ spaces = (size_t)nspaces->car;
+ heredoc_count_indent(hinf, str, len, spaces, &offset);
+ pair->car = (node*)(str + offset);
+ pair->cdr = (node*)(len - offset);
+ }
+ indented = indented->cdr;
+ }
+}
+
+static void
+heredoc_push_indented(parser_state *p, parser_heredoc_info *hinf, node *pair, node *escaped, node *nspaces, mrb_bool empty_line)
+{
+ hinf->indented = push(hinf->indented, cons(pair, cons(escaped, nspaces)));
+ while (nspaces) {
+ size_t tspaces = (size_t)nspaces->car;
+ if ((hinf->indent == ~0U || tspaces < hinf->indent) && !empty_line)
+ hinf->indent = tspaces;
+ nspaces = nspaces->cdr;
+ }
+}
+
static int
parse_string(parser_state *p)
{
@@ -4141,10 +4792,19 @@ parse_string(parser_state *p)
int end = intn(p->lex_strterm->cdr->cdr->cdr);
parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL;
+ mrb_bool unindent = hinf && hinf->remove_indent;
+ mrb_bool head = hinf && hinf->line_head;
+ mrb_bool empty = TRUE;
+ size_t spaces = 0;
+ size_t pos = -1;
+ node *escaped = NULL;
+ node *nspaces = NULL;
+
if (beg == 0) beg = -3; /* should never happen */
if (end == 0) end = -3;
newtok(p);
while ((c = nextc(p)) != end || nest_level != 0) {
+ pos++;
if (hinf && (c == '\n' || c < 0)) {
mrb_bool line_head;
tokadd(p, '\n');
@@ -4164,23 +4824,41 @@ parse_string(parser_state *p)
}
}
if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) {
- if (c < 0) {
- p->parsing_heredoc = NULL;
- }
- else {
- return tHEREDOC_END;
- }
+ heredoc_remove_indent(p, hinf);
+ 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);
+ const char s1[] = "can't find heredoc delimiter \"";
+ const char s2[] = "\" anywhere before EOF";
+
+ if (sizeof(s1)+sizeof(s2)+strlen(hinf->term)+1 >= sizeof(buf)) {
+ yyerror(p, "can't find heredoc delimiter anywhere before EOF");
+ } else {
+ strcpy(buf, s1);
+ strcat(buf, hinf->term);
+ strcat(buf, s2);
+ yyerror(p, buf);
+ }
return 0;
}
- pylval.nd = new_str(p, tok(p), toklen(p));
+ node *nd = new_str(p, tok(p), toklen(p));
+ pylval.nd = nd;
+ if (unindent && head) {
+ nspaces = push(nspaces, nint(spaces));
+ heredoc_push_indented(p, hinf, nd->cdr, escaped, nspaces, empty && line_head);
+ }
return tHD_STRING_MID;
}
+ if (unindent && empty) {
+ if (c == '\t')
+ spaces += 8;
+ else if (ISSPACE(c))
+ ++spaces;
+ else
+ empty = FALSE;
+ }
if (c < 0) {
yyerror(p, "unterminated string meets end of file");
return 0;
@@ -4202,6 +4880,13 @@ parse_string(parser_state *p)
else if (c == '\n') {
p->lineno++;
p->column = 0;
+ if (unindent) {
+ nspaces = push(nspaces, nint(spaces));
+ escaped = push(escaped, nint(pos));
+ pos--;
+ empty = TRUE;
+ spaces = 0;
+ }
if (type & STR_FUNC_ARRAY) {
tokadd(p, '\n');
}
@@ -4251,8 +4936,13 @@ parse_string(parser_state *p)
tokfix(p);
p->lstate = EXPR_BEG;
p->cmd_start = TRUE;
- pylval.nd = new_str(p, tok(p), toklen(p));
+ node *nd = new_str(p, tok(p), toklen(p));
+ pylval.nd = nd;
if (hinf) {
+ if (unindent && head) {
+ nspaces = push(nspaces, nint(spaces));
+ heredoc_push_indented(p, hinf, nd->cdr, escaped, nspaces, FALSE);
+ }
hinf->line_head = FALSE;
return tHD_STRING_PART;
}
@@ -4319,15 +5009,21 @@ parse_string(parser_state *p)
case 'm': f |= 4; break;
case 'u': f |= 16; break;
case 'n': f |= 32; break;
+ case 'o': break;
default: tokadd(p, re_opt); break;
}
}
pushback(p, re_opt);
if (toklen(p)) {
char msg[128];
+
+ strcpy(msg, "unknown regexp option");
tokfix(p);
- snprintf(msg, sizeof(msg), "unknown regexp option%s - %s",
- toklen(p) > 1 ? "s" : "", tok(p));
+ if (toklen(p) > 1) {
+ strcat(msg, "s");
+ }
+ strcat(msg, " - ");
+ strncat(msg, tok(p), sizeof(msg) - strlen(msg) - 1);
yyerror(p, msg);
}
if (f != 0) {
@@ -4358,6 +5054,44 @@ parse_string(parser_state *p)
return tSTRING;
}
+static int
+number_literal_suffix(parser_state *p)
+{
+ int c, result = 0;
+ node *list = 0;
+ int column = p->column;
+ int mask = NUM_SUFFIX_R|NUM_SUFFIX_I;
+
+ while ((c = nextc(p)) != -1) {
+ list = push(list, nint(c));
+
+ if ((mask & NUM_SUFFIX_I) && c == 'i') {
+ result |= (mask & NUM_SUFFIX_I);
+ mask &= ~NUM_SUFFIX_I;
+ /* r after i, rational of complex is disallowed */
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if ((mask & NUM_SUFFIX_R) && c == 'r') {
+ result |= (mask & NUM_SUFFIX_R);
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if (!ISASCII(c) || ISALPHA(c) || c == '_') {
+ p->column = column;
+ if (p->pb) {
+ p->pb = append(list, p->pb);
+ }
+ else {
+ p->pb = list;
+ }
+ return 0;
+ }
+ pushback(p, c);
+ break;
+ }
+ return result;
+}
static int
heredoc_identifier(parser_state *p)
@@ -4365,6 +5099,7 @@ heredoc_identifier(parser_state *p)
int c;
int type = str_heredoc;
mrb_bool indent = FALSE;
+ mrb_bool squiggly = FALSE;
mrb_bool quote = FALSE;
node *newnode;
parser_heredoc_info *info;
@@ -4374,8 +5109,11 @@ heredoc_identifier(parser_state *p)
pushback(p, c);
return 0;
}
- if (c == '-') {
- indent = TRUE;
+ if (c == '-' || c == '~') {
+ if (c == '-')
+ indent = TRUE;
+ if (c == '~')
+ squiggly = TRUE;
c = nextc(p);
}
if (c == '\'' || c == '"') {
@@ -4402,6 +5140,7 @@ heredoc_identifier(parser_state *p)
if (! identchar(c)) {
pushback(p, c);
if (indent) pushback(p, '-');
+ if (squiggly) pushback(p, '~');
return 0;
}
newtok(p);
@@ -4418,7 +5157,10 @@ heredoc_identifier(parser_state *p)
if (! quote)
type |= STR_FUNC_EXPAND;
info->type = (string_type)type;
- info->allow_indent = indent;
+ info->allow_indent = indent || squiggly;
+ info->remove_indent = squiggly;
+ info->indent = ~0U;
+ info->indented = NULL;
info->line_head = TRUE;
info->doc = NULL;
p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode);
@@ -4441,6 +5183,7 @@ static int
parser_yylex(parser_state *p)
{
int32_t c;
+ int nlines = 1;
int space_seen = 0;
int cmd_state;
enum mrb_lex_state_enum last_state;
@@ -4478,63 +5221,79 @@ parser_yylex(parser_state *p)
/* fall through */
case -2: /* end of a file */
case '\n':
- maybe_heredoc:
+ 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);
+ switch (p->lstate) {
+ case EXPR_BEG:
+ case EXPR_FNAME:
+ case EXPR_DOT:
+ case EXPR_CLASS:
+ case EXPR_VALUE:
+ p->lineno++;
+ 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;
+ goto retry;
+ default:
break;
- case '.':
- if ((c = nextc(p)) != '.') {
+ }
+ if (p->parsing_heredoc != NULL) {
+ pylval.num = nlines;
+ return '\n';
+ }
+ while ((c = nextc(p))) {
+ switch (c) {
+ case ' ': case '\t': case '\f': case '\r':
+ case '\13': /* '\v' */
+ space_seen = 1;
+ break;
+ case '#': /* comment as a whitespace */
+ skip(p, '\n');
+ nlines++;
+ break;
+ case '.':
+ if (!peek(p, '.')) {
+ pushback(p, '.');
+ p->lineno+=nlines; nlines=1;
+ goto retry;
+ }
pushback(p, c);
- pushback(p, '.');
- goto retry;
+ goto normal_newline;
+ case '&':
+ if (peek(p, '.')) {
+ pushback(p, '&');
+ p->lineno+=nlines; nlines=1;
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ case -1: /* EOF */
+ case -2: /* end of a file */
+ goto normal_newline;
+ default:
+ pushback(p, c);
+ goto normal_newline;
}
- 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';
+ p->cmd_start = TRUE;
+ p->lstate = EXPR_BEG;
+ pylval.num = nlines;
+ return '\n';
case '*':
if ((c = nextc(p)) == '*') {
if ((c = nextc(p)) == '=') {
- pylval.id = intern("**",2);
+ pylval.id = intern_op(pow);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
pushback(p, c);
if (IS_SPCARG(c)) {
- yywarning(p, "`**' interpreted as argument prefix");
+ yywarning(p, "'**' interpreted as argument prefix");
c = tDSTAR;
}
else if (IS_BEG()) {
@@ -4546,7 +5305,7 @@ parser_yylex(parser_state *p)
}
else {
if (c == '=') {
- pylval.id = intern_c('*');
+ pylval.id = intern_op(mul);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4605,7 +5364,7 @@ parser_yylex(parser_state *p)
c = nextc(p);
} while (!(c < 0 || ISSPACE(c)));
if (c != '\n') skip(p, '\n');
- p->lineno++;
+ p->lineno+=nlines; nlines=1;
p->column = 0;
goto retry;
}
@@ -4662,7 +5421,7 @@ parser_yylex(parser_state *p)
}
if (c == '<') {
if ((c = nextc(p)) == '=') {
- pylval.id = intern("<<",2);
+ pylval.id = intern_op(lshift);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4684,7 +5443,7 @@ parser_yylex(parser_state *p)
}
if (c == '>') {
if ((c = nextc(p)) == '=') {
- pylval.id = intern(">>",2);
+ pylval.id = intern_op(rshift);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4755,7 +5514,10 @@ parser_yylex(parser_state *p)
}
if (c2) {
char buf[256];
- snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2);
+ char cc[] = { (char)c2, '\0' };
+
+ strcpy(buf, "invalid character syntax; use ?\\");
+ strncat(buf, cc, 2);
yyerror(p, buf);
}
}
@@ -4766,10 +5528,10 @@ parser_yylex(parser_state *p)
}
newtok(p);
/* need support UTF-8 if configured */
- if ((isalnum(c) || c == '_')) {
+ if ((ISALNUM(c) || c == '_')) {
int c2 = nextc(p);
pushback(p, c2);
- if ((isalnum(c2) || c2 == '_')) {
+ if ((ISALNUM(c2) || c2 == '_')) {
goto ternary;
}
}
@@ -4789,7 +5551,7 @@ parser_yylex(parser_state *p)
if ((c = nextc(p)) == '&') {
p->lstate = EXPR_BEG;
if ((c = nextc(p)) == '=') {
- pylval.id = intern("&&",2);
+ pylval.id = intern_op(andand);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4801,7 +5563,7 @@ parser_yylex(parser_state *p)
return tANDDOT;
}
else if (c == '=') {
- pylval.id = intern_c('&');
+ pylval.id = intern_op(and);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4828,7 +5590,7 @@ parser_yylex(parser_state *p)
if ((c = nextc(p)) == '|') {
p->lstate = EXPR_BEG;
if ((c = nextc(p)) == '=') {
- pylval.id = intern("||",2);
+ pylval.id = intern_op(oror);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4836,7 +5598,7 @@ parser_yylex(parser_state *p)
return tOROP;
}
if (c == '=') {
- pylval.id = intern_c('|');
+ pylval.id = intern_op(or);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4860,7 +5622,7 @@ parser_yylex(parser_state *p)
return '+';
}
if (c == '=') {
- pylval.id = intern_c('+');
+ pylval.id = intern_op(add);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4888,7 +5650,7 @@ parser_yylex(parser_state *p)
return '-';
}
if (c == '=') {
- pylval.id = intern_c('-');
+ pylval.id = intern_op(sub);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -4909,26 +5671,30 @@ parser_yylex(parser_state *p)
return '-';
case '.':
- p->lstate = EXPR_BEG;
- if ((c = nextc(p)) == '.') {
+ {
+ int is_beg = IS_BEG();
+ p->lstate = EXPR_BEG;
if ((c = nextc(p)) == '.') {
- return tDOT3;
+ if ((c = nextc(p)) == '.') {
+ return is_beg ? tBDOT3 : tDOT3;
+ }
+ pushback(p, c);
+ return is_beg ? tBDOT2 : tDOT2;
}
pushback(p, c);
- return tDOT2;
- }
- pushback(p, c);
- if (c >= 0 && ISDIGIT(c)) {
- yyerror(p, "no .<digit> floating literal anymore; put 0 before dot");
+ if (c >= 0 && ISDIGIT(c)) {
+ yyerror(p, "no .<digit> floating literal anymore; put 0 before dot");
+ }
+ p->lstate = EXPR_DOT;
+ return '.';
}
- 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;
+ int suffix = 0;
is_float = seen_point = seen_e = nondigit = 0;
p->lstate = EXPR_ENDARG;
@@ -4962,7 +5728,8 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 16);
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 16, suffix);
return tINTEGER;
}
if (c == 'b' || c == 'B') {
@@ -4986,7 +5753,8 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 2);
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 2, suffix);
return tINTEGER;
}
if (c == 'd' || c == 'D') {
@@ -5010,7 +5778,8 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 10);
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}
if (c == '_') {
@@ -5043,7 +5812,8 @@ parser_yylex(parser_state *p)
pushback(p, c);
tokfix(p);
if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 8);
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 8, suffix);
return tINTEGER;
}
if (nondigit) {
@@ -5060,7 +5830,8 @@ parser_yylex(parser_state *p)
}
else {
pushback(p, c);
- pylval.nd = new_int(p, "0", 10);
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, "0", 10, suffix);
return tINTEGER;
}
}
@@ -5128,13 +5899,13 @@ parser_yylex(parser_state *p)
pushback(p, c);
if (nondigit) {
trailing_uc:
- yyerror_i(p, "trailing '%c' in number", nondigit);
+ yyerror_c(p, "trailing non digit in number: ", (char)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);
+#ifdef MRB_NO_FLOAT
+ yywarning_s(p, "floating-point numbers are not supported", tok(p));
+ pylval.nd = new_int(p, "0", 10, 0);
return tINTEGER;
#else
double d;
@@ -5143,17 +5914,23 @@ parser_yylex(parser_state *p)
errno = 0;
d = mrb_float_read(tok(p), &endp);
if (d == 0 && endp == tok(p)) {
- yywarning_s(p, "corrupted float value %s", tok(p));
+ yywarning_s(p, "corrupted float value", tok(p));
}
else if (errno == ERANGE) {
- yywarning_s(p, "float %s out of range", tok(p));
+ yywarning_s(p, "float out of range", tok(p));
errno = 0;
}
- pylval.nd = new_float(p, tok(p));
+ suffix = number_literal_suffix(p);
+ if (seen_e && (suffix & NUM_SUFFIX_R)) {
+ pushback(p, 'r');
+ suffix &= ~NUM_SUFFIX_R;
+ }
+ pylval.nd = new_float(p, tok(p), suffix);
return tFLOAT;
#endif
}
- pylval.nd = new_int(p, tok(p), 10);
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}
@@ -5185,14 +5962,14 @@ parser_yylex(parser_state *p)
p->lstate = EXPR_BEG;
return tLABEL_TAG;
}
- if (!ISSPACE(c) || IS_BEG()) {
+ if (IS_END() || ISSPACE(c) || c == '#') {
pushback(p, c);
- p->lstate = EXPR_FNAME;
- return tSYMBEG;
+ p->lstate = EXPR_BEG;
+ return ':';
}
pushback(p, c);
- p->lstate = EXPR_BEG;
- return ':';
+ p->lstate = EXPR_FNAME;
+ return tSYMBEG;
case '/':
if (IS_BEG()) {
@@ -5200,7 +5977,7 @@ parser_yylex(parser_state *p)
return tREGEXP_BEG;
}
if ((c = nextc(p)) == '=') {
- pylval.id = intern_c('/');
+ pylval.id = intern_op(div);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -5219,7 +5996,7 @@ parser_yylex(parser_state *p)
case '^':
if ((c = nextc(p)) == '=') {
- pylval.id = intern_c('^');
+ pylval.id = intern_op(xor);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -5272,6 +6049,7 @@ parser_yylex(parser_state *p)
p->paren_nest++;
if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
p->lstate = EXPR_ARG;
+ p->paren_nest--;
if ((c = nextc(p)) == ']') {
if ((c = nextc(p)) == '=') {
return tASET;
@@ -5316,7 +6094,7 @@ parser_yylex(parser_state *p)
case '\\':
c = nextc(p);
if (c == '\n') {
- p->lineno++;
+ p->lineno+=nlines; nlines=1;
p->column = 0;
space_seen = 1;
goto retry; /* skip \\n */
@@ -5337,7 +6115,7 @@ parser_yylex(parser_state *p)
}
else {
term = nextc(p);
- if (isalnum(term)) {
+ if (ISALNUM(term)) {
yyerror(p, "unknown type of %string");
return 0;
}
@@ -5396,7 +6174,7 @@ parser_yylex(parser_state *p)
}
}
if ((c = nextc(p)) == '=') {
- pylval.id = intern_c('%');
+ pylval.id = intern_op(mod);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
@@ -5450,7 +6228,7 @@ parser_yylex(parser_state *p)
tokadd(p, '$');
tokadd(p, c);
tokfix(p);
- pylval.id = intern_cstr(tok(p));
+ pylval.id = intern(tok(p), toklen(p));
return tGVAR;
case '-':
@@ -5460,7 +6238,7 @@ parser_yylex(parser_state *p)
pushback(p, c);
gvar:
tokfix(p);
- pylval.id = intern_cstr(tok(p));
+ pylval.id = intern(tok(p), toklen(p));
return tGVAR;
case '&': /* $&: last match */
@@ -5481,14 +6259,14 @@ parser_yylex(parser_state *p)
do {
tokadd(p, c);
c = nextc(p);
- } while (c >= 0 && isdigit(c));
+ } 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);
+ yyerror(p, "capture group index must be <= " MRB_STRINGIZE(INT_MAX));
return 0;
}
pylval.nd = new_nth_ref(p, (int)n);
@@ -5523,12 +6301,12 @@ parser_yylex(parser_state *p)
}
return 0;
}
- else if (isdigit(c)) {
+ else if (ISDIGIT(c)) {
if (p->tidx == 1) {
- yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
+ yyerror_c(p, "wrong instance variable name: @", c);
}
else {
- yyerror_i(p, "'@@%c' is not allowed as a class variable name", c);
+ yyerror_c(p, "wrong class variable name: @@", c);
}
return 0;
}
@@ -5544,7 +6322,15 @@ parser_yylex(parser_state *p)
default:
if (!identchar(c)) {
- yyerror_i(p, "Invalid char '\\x%02X' in expression", c);
+ char buf[36];
+ const char s[] = "Invalid char in expression: 0x";
+ const char hexdigits[] = "0123456789ABCDEF";
+
+ strcpy(buf, s);
+ buf[sizeof(s)-1] = hexdigits[(c & 0xf0) >> 4];
+ buf[sizeof(s)] = hexdigits[(c & 0x0f)];
+ buf[sizeof(s)+1] = 0;
+ yyerror(p, buf);
goto retry;
}
@@ -5590,6 +6376,39 @@ parser_yylex(parser_state *p)
result = tIVAR;
break;
+ case '_':
+ if (p->lstate != EXPR_FNAME && toklen(p) == 2 && ISDIGIT(tok(p)[1]) && p->nvars) {
+ int n = tok(p)[1] - '0';
+ int nvar;
+
+ if (n > 0) {
+ node *nvars = p->nvars->cdr;
+
+ while (nvars) {
+ nvar = intn(nvars->car);
+ if (nvar == -2) break; /* top of the scope */
+ if (nvar > 0) {
+ yywarning(p, "numbered parameter used in outer block");
+ break;
+ }
+ nvars->car = nint(-1);
+ nvars = nvars->cdr;
+ }
+ nvar = intn(p->nvars->car);
+ if (nvar == -1) {
+ yywarning(p, "numbered parameter used in inner block");
+ }
+ if (nvar >= -1) {
+ pylval.num = n;
+ p->lstate = EXPR_END;
+ return tNUMPARAM;
+ }
+ else {
+ yywarning(p, "identifier for numbered parameter; consider another name");
+ }
+ }
+ }
+ /* fall through */
default:
if (toklast(p) == '!' || toklast(p) == '?') {
result = tFID;
@@ -5605,6 +6424,15 @@ parser_yylex(parser_state *p)
else {
pushback(p, c);
}
+ 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;
@@ -5618,7 +6446,7 @@ parser_yylex(parser_state *p)
if (IS_LABEL_SUFFIX(0)) {
p->lstate = EXPR_END;
tokfix(p);
- pylval.id = intern_cstr(tok(p));
+ pylval.id = intern(tok(p), toklen(p));
return tIDENTIFIER;
}
}
@@ -5677,10 +6505,10 @@ parser_yylex(parser_state *p)
}
}
{
- mrb_sym ident = intern_cstr(tok(p));
+ mrb_sym ident = intern(tok(p), toklen(p));
pylval.id = ident;
- if (last_state != EXPR_DOT && islower(tok(p)[0]) && local_var_p(p, ident)) {
+ if (last_state != EXPR_DOT && ISLOWER(tok(p)[0]) && local_var_p(p, ident)) {
p->lstate = EXPR_END;
}
}
@@ -5711,7 +6539,7 @@ parser_init_cxt(parser_state *p, mrbc_context *cxt)
}
p->capture_errors = cxt->capture_errors;
p->no_optimize = cxt->no_optimize;
- p->on_eval = cxt->on_eval;
+ p->upper = cxt->upper;
if (cxt->partial_hook) {
p->cxt = cxt;
}
@@ -5810,7 +6638,7 @@ mrb_parser_new(mrb_state *mrb)
p->pool = pool;
p->s = p->send = NULL;
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
p->f = NULL;
#endif
@@ -5883,24 +6711,38 @@ mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser
}
MRB_API void
+mrbc_cleanup_local_variables(mrb_state *mrb, mrbc_context *c)
+{
+ if (c->syms) {
+ mrb_free(mrb, c->syms);
+ c->syms = NULL;
+ c->slen = 0;
+ }
+}
+
+MRB_API void
mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
{
mrb_sym sym;
- size_t i;
+ uint16_t i;
mrb_sym* new_table;
sym = mrb_intern_cstr(p->mrb, f);
- p->filename = mrb_sym2name_len(p->mrb, sym, NULL);
+ p->filename_sym = sym;
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;
+ p->current_filename_index = i;
return;
}
}
- p->current_filename_index = (int)p->filename_table_length++;
+ 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) {
@@ -5910,28 +6752,40 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
p->filename_table[p->filename_table_length - 1] = sym;
}
-MRB_API char const*
+MRB_API mrb_sym
mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
- if (idx >= p->filename_table_length) { return NULL; }
+ if (idx >= p->filename_table_length) return 0;
else {
- return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL);
+ return p->filename_table[idx];
}
}
-#ifndef MRB_DISABLE_STDIO
-MRB_API parser_state*
-mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
+#ifndef MRB_NO_STDIO
+static struct mrb_parser_state *
+mrb_parse_file_continue(mrb_state *mrb, FILE *f, const void *prebuf, size_t prebufsize, mrbc_context *c)
{
parser_state *p;
p = mrb_parser_new(mrb);
if (!p) return NULL;
- p->s = p->send = NULL;
+ if (prebuf) {
+ p->s = (const char *)prebuf;
+ p->send = (const char *)prebuf + prebufsize;
+ }
+ else {
+ p->s = p->send = NULL;
+ }
p->f = f;
mrb_parser_parse(p, c);
return p;
}
+
+MRB_API parser_state*
+mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
+{
+ return mrb_parse_file_continue(mrb, f, NULL, 0, c);
+}
#endif
MRB_API parser_state*
@@ -5960,7 +6814,7 @@ 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;
+ mrb_int keep = 0;
if (!p) {
return mrb_undef_value();
@@ -5969,17 +6823,18 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
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));
+ strcpy(buf, "line ");
+ dump_int(p->error_buffer[0].lineno, buf+5);
+ strcat(buf, ": ");
+ strncat(buf, p->error_buffer[0].message, sizeof(buf) - strlen(buf) - 1);
+ mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, strlen(buf)));
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->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SYNTAX_ERROR, "syntax error"));
}
mrb_parser_free(p);
return mrb_undef_value();
@@ -5989,7 +6844,7 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
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"));
+ mrb->exc = mrb_obj_ptr(mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "codegen error"));
}
return mrb_undef_value();
}
@@ -6008,14 +6863,14 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
}
MRB_PROC_SET_TARGET_CLASS(proc, target);
if (mrb->c->ci) {
- mrb->c->ci->target_class = target;
+ mrb_vm_ci_target_class_set(mrb->c->ci, 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
+#ifndef MRB_NO_STDIO
MRB_API mrb_value
mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c)
{
@@ -6027,6 +6882,55 @@ mrb_load_file(mrb_state *mrb, FILE *f)
{
return mrb_load_file_cxt(mrb, f, NULL);
}
+
+#define DETECT_SIZE 64
+
+/*
+ * In order to be recognized as a `.mrb` file, the following three points must be satisfied:
+ * - File starts with "RITE"
+ * - At least `sizeof(struct rite_binary_header)` bytes can be read
+ * - `NUL` is included in the first 64 bytes of the file
+ */
+MRB_API mrb_value
+mrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, mrbc_context *c)
+{
+ union {
+ char b[DETECT_SIZE];
+ struct rite_binary_header h;
+ } leading;
+ size_t bufsize;
+
+ if (mrb == NULL || fp == NULL) {
+ return mrb_nil_value();
+ }
+
+ bufsize = fread(leading.b, sizeof(char), sizeof(leading), fp);
+ if (bufsize < sizeof(leading.h) ||
+ memcmp(leading.h.binary_ident, RITE_BINARY_IDENT, sizeof(leading.h.binary_ident)) != 0 ||
+ memchr(leading.b, '\0', bufsize) == NULL) {
+ return mrb_load_exec(mrb, mrb_parse_file_continue(mrb, fp, leading.b, bufsize, c), c);
+ }
+ else {
+ size_t binsize;
+ uint8_t *bin;
+ mrb_value bin_obj = mrb_nil_value(); /* temporary string object */
+ mrb_value result;
+
+ binsize = bin_to_uint32(leading.h.binary_size);
+ bin_obj = mrb_str_new(mrb, NULL, binsize);
+ bin = (uint8_t *)RSTRING_PTR(bin_obj);
+ memcpy(bin, leading.b, bufsize);
+ if (binsize > bufsize &&
+ fread(bin + bufsize, binsize - bufsize, 1, fp) == 0) {
+ binsize = bufsize;
+ /* The error is reported by mrb_load_irep_buf_cxt() */
+ }
+
+ result = mrb_load_irep_buf_cxt(mrb, bin, binsize, c);
+ if (mrb_string_p(bin_obj)) mrb_str_resize(mrb, bin_obj, 0);
+ return result;
+ }
+}
#endif
MRB_API mrb_value
@@ -6053,7 +6957,7 @@ mrb_load_string(mrb_state *mrb, const char *s)
return mrb_load_string_cxt(mrb, s, NULL);
}
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
static void
dump_prefix(node *tree, int offset)
@@ -6091,7 +6995,7 @@ dump_args(mrb_state *mrb, node *n, int offset)
while (n2) {
dump_prefix(n2, offset+2);
- printf("%s=\n", mrb_sym2name(mrb, sym(n2->car->car)));
+ printf("%s=\n", mrb_sym_name(mrb, sym(n2->car->car)));
mrb_parser_dump(mrb, n2->car->cdr, offset+3);
n2 = n2->cdr;
}
@@ -6100,7 +7004,7 @@ dump_args(mrb_state *mrb, node *n, int offset)
n = n->cdr;
if (n->car) {
dump_prefix(n, offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
+ printf("rest=*%s\n", mrb_sym_name(mrb, sym(n->car)));
}
n = n->cdr;
if (n->car) {
@@ -6116,12 +7020,34 @@ dump_args(mrb_state *mrb, node *n, int offset)
}
}
+/*
+ * This function restores the GC arena on return.
+ * For this reason, if a process that further generates an object is
+ * performed at the caller, the string pointer returned as the return
+ * value may become invalid.
+ */
+static const char*
+str_dump(mrb_state *mrb, const char *str, int len)
+{
+ int ai = mrb_gc_arena_save(mrb);
+ mrb_value s;
+# if INT_MAX > MRB_INT_MAX / 4
+ /* check maximum length with "\xNN" character */
+ if (len > MRB_INT_MAX / 4) {
+ len = MRB_INT_MAX / 4;
+ }
+# endif
+ s = mrb_str_new(mrb, str, (mrb_int)len);
+ s = mrb_str_dump(mrb, s);
+ mrb_gc_arena_restore(mrb, ai);
+ return RSTRING_PTR(s);
+}
#endif
void
mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
{
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
int nodetype;
if (!tree) return;
@@ -6319,7 +7245,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
while (n2) {
if (n2->car) {
if (!first_lval) printf(", ");
- printf("%s", mrb_sym2name(mrb, sym(n2->car)));
+ printf("%s", mrb_sym_name(mrb, sym(n2->car)));
first_lval = FALSE;
}
n2 = n2->cdr;
@@ -6347,7 +7273,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
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)),
+ mrb_sym_dump(mrb, sym(tree->cdr->car)),
intn(tree->cdr->car));
tree = tree->cdr->cdr->car;
if (tree) {
@@ -6378,11 +7304,11 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
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)));
+ printf("::%s\n", mrb_sym_name(mrb, sym(tree->cdr)));
break;
case NODE_COLON3:
- printf("NODE_COLON3: ::%s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_COLON3: ::%s\n", mrb_sym_name(mrb, sym(tree)));
break;
case NODE_ARRAY:
@@ -6448,7 +7374,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
if (n2->car) {
dump_prefix(n2, offset+2);
printf("rest:\n");
- if (n2->car == (node*)-1) {
+ if (n2->car == nint(-1)) {
dump_prefix(n2, offset+2);
printf("(empty)\n");
}
@@ -6478,7 +7404,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
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));
+ printf("op='%s' (%d)\n", mrb_sym_name(mrb, sym(tree->car)), intn(tree->car));
tree = tree->cdr;
mrb_parser_dump(mrb, tree->car, offset+1);
break;
@@ -6530,23 +7456,27 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_LVAR:
- printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_LVAR %s\n", mrb_sym_name(mrb, sym(tree)));
break;
case NODE_GVAR:
- printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_GVAR %s\n", mrb_sym_name(mrb, sym(tree)));
break;
case NODE_IVAR:
- printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_IVAR %s\n", mrb_sym_name(mrb, sym(tree)));
break;
case NODE_CVAR:
- printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_CVAR %s\n", mrb_sym_name(mrb, sym(tree)));
+ break;
+
+ case NODE_NVAR:
+ printf("NODE_NVAR %d\n", intn(tree));
break;
case NODE_CONST:
- printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_CONST %s\n", mrb_sym_name(mrb, sym(tree)));
break;
case NODE_MATCH:
@@ -6568,7 +7498,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_ARG:
- printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_ARG %s\n", mrb_sym_name(mrb, sym(tree)));
break;
case NODE_BLOCK_ARG:
@@ -6585,25 +7515,25 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_NEGATE:
- printf("NODE_NEGATE\n");
+ 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));
+ printf("NODE_STR %s len %d\n", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), intn(tree->cdr));
break;
case NODE_DSTR:
- printf("NODE_DSTR\n");
+ 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));
+ printf("NODE_XSTR %s len %d\n", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), intn(tree->cdr));
break;
case NODE_DXSTR:
- printf("NODE_DXSTR\n");
+ printf("NODE_DXSTR:\n");
dump_recur(mrb, tree, offset+1);
break;
@@ -6612,7 +7542,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_DREGX:
- printf("NODE_DREGX\n");
+ printf("NODE_DREGX:\n");
dump_recur(mrb, tree->car, offset+1);
dump_prefix(tree, offset);
printf("tail: %s\n", (char*)tree->cdr->cdr->car);
@@ -6627,10 +7557,29 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_SYM:
- printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)),
+ printf("NODE_SYM :%s (%d)\n", mrb_sym_dump(mrb, sym(tree)),
intn(tree));
break;
+ case NODE_DSYM:
+ printf("NODE_DSYM:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_WORDS:
+ printf("NODE_WORDS:\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_SYMBOLS:
+ printf("NODE_SYMBOLS:\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_LITERAL_DELIM:
+ printf("NODE_LITERAL_DELIM\n");
+ break;
+
case NODE_SELF:
printf("NODE_SELF\n");
break;
@@ -6649,8 +7598,8 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_ALIAS:
printf("NODE_ALIAS %s %s:\n",
- mrb_sym2name(mrb, sym(tree->car)),
- mrb_sym2name(mrb, sym(tree->cdr)));
+ mrb_sym_dump(mrb, sym(tree->car)),
+ mrb_sym_dump(mrb, sym(tree->cdr)));
break;
case NODE_UNDEF:
@@ -6658,7 +7607,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
{
node *t = tree;
while (t) {
- printf(" %s", mrb_sym2name(mrb, sym(t->car)));
+ printf(" %s", mrb_sym_dump(mrb, sym(t->car)));
t = t->cdr;
}
}
@@ -6667,18 +7616,18 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_CLASS:
printf("NODE_CLASS:\n");
- if (tree->car->car == (node*)0) {
+ if (tree->car->car == nint(0)) {
dump_prefix(tree, offset+1);
- printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
}
- else if (tree->car->car == (node*)1) {
+ else if (tree->car->car == nint(1)) {
dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ printf("::%s\n", mrb_sym_name(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)));
+ printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
}
if (tree->cdr->car) {
dump_prefix(tree, offset+1);
@@ -6692,18 +7641,18 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_MODULE:
printf("NODE_MODULE:\n");
- if (tree->car->car == (node*)0) {
+ if (tree->car->car == nint(0)) {
dump_prefix(tree, offset+1);
- printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
}
- else if (tree->car->car == (node*)1) {
+ else if (tree->car->car == nint(1)) {
dump_prefix(tree, offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ printf("::%s\n", mrb_sym_name(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)));
+ printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
}
dump_prefix(tree, offset+1);
printf("body:\n");
@@ -6721,7 +7670,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_DEF:
printf("NODE_DEF:\n");
dump_prefix(tree, offset+1);
- printf("%s\n", mrb_sym2name(mrb, sym(tree->car)));
+ printf("%s\n", mrb_sym_dump(mrb, sym(tree->car)));
tree = tree->cdr;
{
node *n2 = tree->car;
@@ -6734,7 +7683,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
while (n2) {
if (n2->car) {
if (!first_lval) printf(", ");
- printf("%s", mrb_sym2name(mrb, sym(n2->car)));
+ printf("%s", mrb_sym_name(mrb, sym(n2->car)));
first_lval = FALSE;
}
n2 = n2->cdr;
@@ -6754,7 +7703,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
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)));
+ printf(":%s\n", mrb_sym_dump(mrb, sym(tree->car)));
tree = tree->cdr->cdr;
if (tree->car) {
dump_args(mrb, tree->car, offset+1);
@@ -6791,17 +7740,17 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
tree = tree->cdr;
if (tree->car) {
dump_prefix(tree, offset+1);
- printf("block='%s'\n", mrb_sym2name(mrb, sym(tree->car)));
+ printf("block='%s'\n", mrb_sym_name(mrb, sym(tree->car)));
}
break;
case NODE_KW_ARG:
- printf("NODE_KW_ARG %s\n", mrb_sym2name(mrb, sym(tree->car)));
+ printf("NODE_KW_ARG %s:\n", mrb_sym_name(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)));
+ printf("NODE_KW_REST_ARGS %s\n", mrb_sym_name(mrb, sym(tree)));
break;
default:
@@ -6810,3 +7759,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
#endif
}
+
+typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user);
+void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user);
+
+void
+mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user)
+{
+ const mrb_ast_node *n = p->tree;
+ if ((intptr_t)n->car == NODE_SCOPE) {
+ n = n->cdr->car;
+ for (; n; n = n->cdr) {
+ mrb_sym sym = (intptr_t)n->car;
+ if (sym && !func(mrb, sym, user)) break;
+ }
+ }
+}
diff --git a/mrbgems/mruby-compiler/core/y.tab.c b/mrbgems/mruby-compiler/core/y.tab.c
new file mode 100644
index 000000000..9cd36ec7b
--- /dev/null
+++ b/mrbgems/mruby-compiler/core/y.tab.c
@@ -0,0 +1,13969 @@
+/* A Bison parser, made by GNU Bison 3.5.1. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+ Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Undocumented macros, especially those whose name start with YY_,
+ are private implementation details. Do not rely on them. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "3.5.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+
+
+/* First part of user prologue. */
+#line 7 "mrbgems/mruby-compiler/core/parse.y"
+
+#undef PARSER_DEBUG
+#ifdef PARSER_DEBUG
+# define YYDEBUG 1
+#endif
+#define YYSTACK_USE_ALLOCA 1
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/error.h>
+#include <mruby/throw.h>
+#include <mruby/string.h>
+#include <mruby/dump.h>
+#include <mruby/presym.h>
+#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))
+#define typen(x) ((enum node_type)(intptr_t)(x))
+
+#define NUM_SUFFIX_R (1<<0)
+#define NUM_SUFFIX_I (1<<1)
+
+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))
+
+#define intern_op(op) MRB_OPSYM_2(p->mrb, op)
+
+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;
+ if (!b) return a;
+ while (c->cdr) {
+ c = c->cdr;
+ }
+ 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)
+
+static void
+dump_int(uint16_t i, char *s)
+{
+ char *p = s;
+ char *t = s;
+
+ while (i > 0) {
+ *p++ = (i % 10)+'0';
+ i /= 10;
+ }
+ if (p == s) *p++ = '0';
+ *p = 0;
+ p--; /* point the last char */
+ while (t < p) {
+ char c = *t;
+ *t++ = *p;
+ *p-- = c;
+ }
+}
+
+/* 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)
+{
+ const struct RProc *u;
+ 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;
+ }
+
+ u = p->upper;
+ while (u && !MRB_PROC_CFUNC_P(u)) {
+ const struct mrb_irep *ir = u->body.irep;
+ const mrb_sym *v = ir->lv;
+ int i;
+
+ if (v) {
+ for (i=0; i+1 < ir->nlocals; i++) {
+ if (v[i] == sym) return TRUE;
+ }
+ }
+ if (MRB_PROC_SCOPE_P(u)) break;
+ u = u->upper;
+ }
+ 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 void
+local_add_blk(parser_state *p, mrb_sym blk)
+{
+ /* allocate register for block */
+ local_add_f(p, blk ? blk : intern_op(and));
+}
+
+static void
+local_add_kw(parser_state *p, mrb_sym kwd)
+{
+ /* allocate register for keywords hash */
+ local_add_f(p, kwd ? kwd : intern_op(pow));
+}
+
+static node*
+locals_node(parser_state *p)
+{
+ return p->locals ? p->locals->car : NULL;
+}
+
+static void
+nvars_nest(parser_state *p)
+{
+ p->nvars = cons(nint(0), p->nvars);
+}
+
+static void
+nvars_block(parser_state *p)
+{
+ p->nvars = cons(nint(-2), p->nvars);
+}
+
+static void
+nvars_unnest(parser_state *p)
+{
+ p->nvars = p->nvars->cdr;
+}
+
+/* (: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));
+}
+
+/* (:nvar . a) */
+static node*
+new_nvar(parser_state *p, int num)
+{
+ int nvars = intn(p->nvars->car);
+
+ p->nvars->car = nint(nvars > num ? nvars : num);
+ return cons((node*)NODE_NVAR, nint(num));
+}
+
+/* (: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), 0, a, b);
+}
+
+static void
+defn_setup(parser_state *p, node *d, node *a, node *b)
+{
+ node *n = d->cdr->cdr;
+
+ n->car = locals_node(p);
+ p->cmdarg_stack = intn(n->cdr->car);
+ n->cdr->car = a;
+ local_resume(p, n->cdr->cdr->car);
+ n->cdr->cdr->car = 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), 0, a, b);
+}
+
+static void
+defs_setup(parser_state *p, node *d, node *a, node *b)
+{
+ node *n = d->cdr->cdr->cdr;
+
+ n->car = locals_node(p);
+ p->cmdarg_stack = intn(n->cdr->car);
+ n->cdr->car = a;
+ local_resume(p, n->cdr->cdr->car);
+ n->cdr->cdr->car = 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 (typen(n->car->car) == 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;
+ }
+}
+
+static void
+local_add_lv(parser_state *p, node *lv)
+{
+ while (lv) {
+ local_add_f(p, sym(lv->car));
+ lv = lv->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);
+ while (opt) {
+ /* opt: (sym . (opt . lv)) -> (sym . opt) */
+ local_add_lv(p, opt->car->cdr->cdr);
+ opt->car->cdr = opt->car->cdr->car;
+ opt = opt->cdr;
+ }
+ 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;
+
+ if (kws || kwrest) {
+ local_add_kw(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : 0);
+ }
+
+ local_add_blk(p, blk);
+
+ /* 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_lv(p, k->car->cdr->cdr->car->cdr);
+ k->car->cdr->cdr->car = k->car->cdr->cdr->car->car;
+ 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);
+}
+
+/* (:kw_rest_args . a) */
+static node*
+new_kw_rest_args(parser_state *p, node *a)
+{
+ return cons((node*)NODE_KW_REST_ARGS, a);
+}
+
+/* (:block_arg . a) */
+static node*
+new_block_arg(parser_state *p, node *a)
+{
+ return cons((node*)NODE_BLOCK_ARG, a);
+}
+
+static node*
+setup_numparams(parser_state *p, node *a)
+{
+ int nvars = intn(p->nvars->car);
+ if (nvars > 0) {
+ int i;
+ mrb_sym sym;
+ // m || opt || rest || tail
+ if (a && (a->car || (a->cdr && a->cdr->car) || (a->cdr->cdr && a->cdr->cdr->car) || (a->cdr->cdr->cdr->cdr && a->cdr->cdr->cdr->cdr->car))) {
+ yyerror(p, "ordinary parameter is defined");
+ }
+ else if (p->locals) {
+ /* p->locals should not be NULL unless error happens before the point */
+ node* args = 0;
+ for (i = nvars; i > 0; i--) {
+ char buf[3];
+
+ buf[0] = '_';
+ buf[1] = i+'0';
+ buf[2] = '\0';
+ sym = intern_cstr(buf);
+ args = cons(new_arg(p, sym), args);
+ p->locals->car = cons(nsym(sym), p->locals->car);
+ }
+ a = new_args(p, args, 0, 0, 0, 0);
+ }
+ }
+ return a;
+}
+
+/* (:block arg body) */
+static node*
+new_block(parser_state *p, node *a, node *b)
+{
+ a = setup_numparams(p, a);
+ 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));
+}
+
+/* (:masgn mlhs mrhs) no check */
+static node*
+new_masgn_param(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_MASGN, cons(a, b));
+}
+
+/* (:asgn lhs rhs) */
+static node*
+new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
+{
+ void_expr_error(p, b);
+ return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
+}
+
+static node*
+new_imaginary(parser_state *p, node *imaginary)
+{
+ return new_call(p, new_const(p, MRB_SYM_2(p->mrb, Kernel)), MRB_SYM_2(p->mrb, Complex), list1(list2(list3((node*)NODE_INT, (node*)strdup("0"), nint(10)), imaginary)), 1);
+}
+
+static node*
+new_rational(parser_state *p, node *rational)
+{
+ return new_call(p, new_const(p, MRB_SYM_2(p->mrb, Kernel)), MRB_SYM_2(p->mrb, Rational), list1(list1(rational)), 1);
+}
+
+/* (:int . i) */
+static node*
+new_int(parser_state *p, const char *s, int base, int suffix)
+{
+ node* result = list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+ return result;
+}
+
+#ifndef MRB_NO_FLOAT
+/* (:float . i) */
+static node*
+new_float(parser_state *p, const char *s, int suffix)
+{
+ node* result = cons((node*)NODE_FLOAT, (node*)strdup(s));
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+ return result;
+}
+#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);
+}
+
+static int
+string_node_p(node *n)
+{
+ return (int)(typen(n->car) == NODE_STR);
+}
+
+static node*
+composite_string_node(parser_state *p, node *a, node *b)
+{
+ size_t newlen = (size_t)a->cdr + (size_t)b->cdr;
+ char *str = (char*)mrb_pool_realloc(p->pool, a->car, (size_t)a->cdr + 1, newlen + 1);
+ memcpy(str + (size_t)a->cdr, b->car, (size_t)b->cdr);
+ str[newlen] = '\0';
+ a->car = (node*)str;
+ a->cdr = (node*)newlen;
+ cons_free(b);
+ return a;
+}
+
+static node*
+concat_string(parser_state *p, node *a, node *b)
+{
+ if (string_node_p(a)) {
+ if (string_node_p(b)) {
+ /* a == NODE_STR && b == NODE_STR */
+ composite_string_node(p, a->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+ else {
+ /* a == NODE_STR && b == NODE_DSTR */
+
+ if (string_node_p(b->cdr->car)) {
+ /* a == NODE_STR && b->[NODE_STR, ...] */
+ composite_string_node(p, a->cdr, b->cdr->car->cdr);
+ cons_free(b->cdr->car);
+ b->cdr->car = a;
+ return b;
+ }
+ }
+ }
+ else {
+ node *c; /* last node of a */
+ for (c = a; c->cdr != NULL; c = c->cdr) ;
+
+ if (string_node_p(b)) {
+ /* a == NODE_DSTR && b == NODE_STR */
+ if (string_node_p(c->car)) {
+ /* a->[..., NODE_STR] && b == NODE_STR */
+ composite_string_node(p, c->car->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+
+ push(a, b);
+ return a;
+ }
+ else {
+ /* a == NODE_DSTR && b == NODE_DSTR */
+ if (string_node_p(c->car) && string_node_p(b->cdr->car)) {
+ /* a->[..., NODE_STR] && b->[NODE_STR, ...] */
+ node *d = b->cdr;
+ cons_free(b);
+ composite_string_node(p, c->car->cdr, d->car->cdr);
+ cons_free(d->car);
+ c->cdr = d->cdr;
+ cons_free(d);
+ return a;
+ }
+ else {
+ c->cdr = b->cdr;
+ cons_free(b);
+ return a;
+ }
+ }
+ }
+
+ return new_dstr(p, list2(a, b));
+}
+
+/* (: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
+endless_method_name(parser_state *p, node *defn)
+{
+ mrb_sym sym = sym(defn->cdr->car);
+ mrb_int len;
+ const char *name = mrb_sym_name_len(p->mrb, sym, &len);
+
+ if (len > 1 && name[len-1] == '=') {
+ for (int i=0; i<len-1; i++) {
+ if (!identchar(name[i])) return;
+ }
+ yyerror(p, "setter method cannot be defined by endless method definition");
+ }
+}
+
+static void
+call_with_block(parser_state *p, node *a, node *b)
+{
+ node *n;
+
+ switch (typen(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(nint(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;
+ 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 ----------------------------- */
+
+
+#line 1453 "mrbgems/mruby-compiler/core/y.tab.c"
+
+# ifndef YY_CAST
+# ifdef __cplusplus
+# define YY_CAST(Type, Val) static_cast<Type> (Val)
+# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+# else
+# define YY_CAST(Type, Val) ((Type) (Val))
+# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+# endif
+# endif
+# ifndef YY_NULLPTR
+# if defined __cplusplus
+# if 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# else
+# define YY_NULLPTR ((void*)0)
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ keyword_class = 258,
+ keyword_module = 259,
+ keyword_def = 260,
+ keyword_begin = 261,
+ keyword_if = 262,
+ keyword_unless = 263,
+ keyword_while = 264,
+ keyword_until = 265,
+ keyword_for = 266,
+ keyword_undef = 267,
+ keyword_rescue = 268,
+ keyword_ensure = 269,
+ keyword_end = 270,
+ keyword_then = 271,
+ keyword_elsif = 272,
+ keyword_else = 273,
+ keyword_case = 274,
+ keyword_when = 275,
+ keyword_break = 276,
+ keyword_next = 277,
+ keyword_redo = 278,
+ keyword_retry = 279,
+ keyword_in = 280,
+ keyword_do = 281,
+ keyword_do_cond = 282,
+ keyword_do_block = 283,
+ keyword_do_LAMBDA = 284,
+ keyword_return = 285,
+ keyword_yield = 286,
+ keyword_super = 287,
+ keyword_self = 288,
+ keyword_nil = 289,
+ keyword_true = 290,
+ keyword_false = 291,
+ keyword_and = 292,
+ keyword_or = 293,
+ keyword_not = 294,
+ modifier_if = 295,
+ modifier_unless = 296,
+ modifier_while = 297,
+ modifier_until = 298,
+ modifier_rescue = 299,
+ keyword_alias = 300,
+ keyword_BEGIN = 301,
+ keyword_END = 302,
+ keyword__LINE__ = 303,
+ keyword__FILE__ = 304,
+ keyword__ENCODING__ = 305,
+ tIDENTIFIER = 306,
+ tFID = 307,
+ tGVAR = 308,
+ tIVAR = 309,
+ tCONSTANT = 310,
+ tCVAR = 311,
+ tLABEL_TAG = 312,
+ tINTEGER = 313,
+ tFLOAT = 314,
+ tCHAR = 315,
+ tXSTRING = 316,
+ tREGEXP = 317,
+ tSTRING = 318,
+ tSTRING_PART = 319,
+ tSTRING_MID = 320,
+ tNTH_REF = 321,
+ tBACK_REF = 322,
+ tREGEXP_END = 323,
+ tNUMPARAM = 324,
+ tUPLUS = 325,
+ tUMINUS = 326,
+ tCMP = 327,
+ tEQ = 328,
+ tEQQ = 329,
+ tNEQ = 330,
+ tGEQ = 331,
+ tLEQ = 332,
+ tANDOP = 333,
+ tOROP = 334,
+ tMATCH = 335,
+ tNMATCH = 336,
+ tDOT2 = 337,
+ tDOT3 = 338,
+ tBDOT2 = 339,
+ tBDOT3 = 340,
+ tAREF = 341,
+ tASET = 342,
+ tLSHFT = 343,
+ tRSHFT = 344,
+ tCOLON2 = 345,
+ tCOLON3 = 346,
+ tOP_ASGN = 347,
+ tASSOC = 348,
+ tLPAREN = 349,
+ tLPAREN_ARG = 350,
+ tRPAREN = 351,
+ tLBRACK = 352,
+ tLBRACE = 353,
+ tLBRACE_ARG = 354,
+ tSTAR = 355,
+ tPOW = 356,
+ tDSTAR = 357,
+ tAMPER = 358,
+ tLAMBDA = 359,
+ tANDDOT = 360,
+ tSYMBEG = 361,
+ tSTRING_BEG = 362,
+ tXSTRING_BEG = 363,
+ tSTRING_DVAR = 364,
+ tREGEXP_BEG = 365,
+ tWORDS_BEG = 366,
+ tSYMBOLS_BEG = 367,
+ tLAMBEG = 368,
+ tHEREDOC_BEG = 369,
+ tHEREDOC_END = 370,
+ tLITERAL_DELIM = 371,
+ tHD_LITERAL_DELIM = 372,
+ tHD_STRING_PART = 373,
+ tHD_STRING_MID = 374,
+ tLOWEST = 375,
+ tUMINUS_NUM = 376,
+ tLAST_TOKEN = 377
+ };
+#endif
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+union YYSTYPE
+{
+#line 1395 "mrbgems/mruby-compiler/core/parse.y"
+
+ node *nd;
+ mrb_sym id;
+ int num;
+ stack_type stack;
+ const struct vtable *vars;
+
+#line 1633 "mrbgems/mruby-compiler/core/y.tab.c"
+
+};
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+int yyparse (parser_state *p);
+
+
+
+
+
+#ifdef short
+# undef short
+#endif
+
+/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure
+ <limits.h> and (if available) <stdint.h> are included
+ so that the code can choose integer types of a good width. */
+
+#ifndef __PTRDIFF_MAX__
+# include <limits.h> /* INFRINGES ON USER NAME SPACE */
+# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+# include <stdint.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_STDINT_H
+# endif
+#endif
+
+/* Narrow types that promote to a signed type and that can represent a
+ signed or unsigned integer of at least N bits. In tables they can
+ save space and decrease cache pressure. Promoting to a signed type
+ helps avoid bugs in integer arithmetic. */
+
+#ifdef __INT_LEAST8_MAX__
+typedef __INT_LEAST8_TYPE__ yytype_int8;
+#elif defined YY_STDINT_H
+typedef int_least8_t yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef __INT_LEAST16_MAX__
+typedef __INT_LEAST16_TYPE__ yytype_int16;
+#elif defined YY_STDINT_H
+typedef int_least16_t yytype_int16;
+#else
+typedef short yytype_int16;
+#endif
+
+#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST8_TYPE__ yytype_uint8;
+#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
+ && UINT_LEAST8_MAX <= INT_MAX)
+typedef uint_least8_t yytype_uint8;
+#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX
+typedef unsigned char yytype_uint8;
+#else
+typedef short yytype_uint8;
+#endif
+
+#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST16_TYPE__ yytype_uint16;
+#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \
+ && UINT_LEAST16_MAX <= INT_MAX)
+typedef uint_least16_t yytype_uint16;
+#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX
+typedef unsigned short yytype_uint16;
+#else
+typedef int yytype_uint16;
+#endif
+
+#ifndef YYPTRDIFF_T
+# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__
+# define YYPTRDIFF_T __PTRDIFF_TYPE__
+# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__
+# elif defined PTRDIFF_MAX
+# ifndef ptrdiff_t
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# endif
+# define YYPTRDIFF_T ptrdiff_t
+# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX
+# else
+# define YYPTRDIFF_T long
+# define YYPTRDIFF_MAXIMUM LONG_MAX
+# endif
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM \
+ YY_CAST (YYPTRDIFF_T, \
+ (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \
+ ? YYPTRDIFF_MAXIMUM \
+ : YY_CAST (YYSIZE_T, -1)))
+
+#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X))
+
+/* Stored state numbers (used for stacks). */
+typedef yytype_int16 yy_state_t;
+
+/* State numbers in computations. */
+typedef int yy_state_fast_t;
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+# define YY_ATTRIBUTE_PURE
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+# define YY_ATTRIBUTE_UNUSED
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END \
+ _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
+
+#define YY_ASSERT(E) ((void) (0 && (E)))
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's 'empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yy_state_t yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYPTRDIFF_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / YYSIZEOF (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYPTRDIFF_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 3
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 13314
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 149
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 177
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 611
+/* YYNSTATES -- Number of states. */
+#define YYNSTATES 1075
+
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 377
+
+
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+ as returned by yylex, with out-of-bounds checking. */
+#define YYTRANSLATE(YYX) \
+ (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+ as returned by yylex. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 148, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 135, 2, 2, 2, 133, 128, 2,
+ 144, 145, 131, 129, 142, 130, 147, 132, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 123, 146,
+ 125, 121, 124, 122, 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, 140, 2, 141, 127, 2, 143, 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, 138, 126, 139, 136, 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, 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, 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, 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, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 134, 137
+};
+
+#if YYDEBUG
+ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
+static const yytype_int16 yyrline[] =
+{
+ 0, 1566, 1566, 1566, 1577, 1583, 1587, 1592, 1596, 1602,
+ 1604, 1603, 1617, 1644, 1650, 1654, 1659, 1663, 1669, 1669,
+ 1673, 1677, 1681, 1685, 1689, 1693, 1697, 1702, 1703, 1707,
+ 1711, 1715, 1719, 1726, 1729, 1733, 1737, 1741, 1745, 1749,
+ 1754, 1758, 1767, 1777, 1786, 1796, 1803, 1804, 1808, 1812,
+ 1813, 1817, 1821, 1825, 1829, 1833, 1843, 1842, 1857, 1866,
+ 1867, 1870, 1871, 1878, 1877, 1892, 1896, 1901, 1905, 1910,
+ 1914, 1919, 1923, 1927, 1931, 1935, 1941, 1945, 1951, 1952,
+ 1958, 1962, 1966, 1970, 1974, 1978, 1982, 1986, 1990, 1994,
+ 2000, 2001, 2007, 2011, 2017, 2021, 2027, 2031, 2035, 2039,
+ 2043, 2047, 2053, 2059, 2066, 2070, 2074, 2078, 2082, 2086,
+ 2092, 2098, 2103, 2109, 2113, 2116, 2120, 2124, 2131, 2132,
+ 2133, 2134, 2139, 2146, 2147, 2150, 2154, 2154, 2160, 2161,
+ 2162, 2163, 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171,
+ 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2181,
+ 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2192, 2192,
+ 2192, 2193, 2193, 2194, 2194, 2194, 2195, 2195, 2195, 2195,
+ 2196, 2196, 2196, 2197, 2197, 2197, 2198, 2198, 2198, 2198,
+ 2199, 2199, 2199, 2199, 2200, 2200, 2200, 2200, 2201, 2201,
+ 2201, 2201, 2202, 2202, 2202, 2202, 2203, 2203, 2206, 2210,
+ 2214, 2218, 2222, 2226, 2230, 2235, 2240, 2245, 2249, 2253,
+ 2257, 2261, 2265, 2269, 2273, 2277, 2281, 2285, 2289, 2293,
+ 2297, 2301, 2305, 2309, 2313, 2317, 2321, 2325, 2329, 2333,
+ 2337, 2341, 2345, 2349, 2353, 2357, 2361, 2365, 2369, 2373,
+ 2377, 2381, 2385, 2389, 2393, 2402, 2412, 2421, 2431, 2437,
+ 2438, 2443, 2447, 2454, 2458, 2466, 2470, 2486, 2512, 2513,
+ 2516, 2517, 2518, 2523, 2528, 2535, 2541, 2546, 2551, 2556,
+ 2563, 2563, 2574, 2580, 2584, 2590, 2591, 2594, 2600, 2606,
+ 2611, 2618, 2623, 2628, 2635, 2636, 2637, 2638, 2639, 2640,
+ 2641, 2642, 2646, 2651, 2650, 2662, 2666, 2661, 2671, 2671,
+ 2675, 2679, 2683, 2687, 2692, 2697, 2701, 2705, 2709, 2713,
+ 2717, 2718, 2724, 2730, 2723, 2742, 2750, 2758, 2758, 2758,
+ 2765, 2765, 2765, 2772, 2778, 2783, 2785, 2782, 2794, 2792,
+ 2810, 2815, 2808, 2832, 2830, 2846, 2856, 2867, 2871, 2875,
+ 2879, 2885, 2892, 2893, 2894, 2897, 2898, 2901, 2902, 2910,
+ 2911, 2917, 2921, 2924, 2928, 2932, 2936, 2941, 2945, 2949,
+ 2953, 2959, 2958, 2968, 2972, 2976, 2980, 2986, 2991, 2996,
+ 3000, 3004, 3008, 3012, 3016, 3020, 3024, 3028, 3032, 3036,
+ 3040, 3044, 3048, 3052, 3058, 3063, 3070, 3070, 3074, 3079,
+ 3086, 3090, 3096, 3097, 3100, 3105, 3108, 3112, 3118, 3122,
+ 3129, 3128, 3143, 3153, 3157, 3162, 3169, 3173, 3177, 3181,
+ 3185, 3189, 3193, 3197, 3201, 3208, 3207, 3222, 3221, 3237,
+ 3245, 3254, 3257, 3264, 3267, 3271, 3272, 3275, 3279, 3282,
+ 3286, 3289, 3290, 3291, 3292, 3295, 3296, 3302, 3303, 3304,
+ 3308, 3314, 3315, 3321, 3326, 3325, 3336, 3340, 3346, 3350,
+ 3356, 3360, 3366, 3369, 3370, 3373, 3379, 3385, 3386, 3389,
+ 3396, 3395, 3409, 3413, 3420, 3425, 3432, 3438, 3439, 3440,
+ 3441, 3442, 3446, 3452, 3456, 3462, 3463, 3464, 3468, 3474,
+ 3478, 3482, 3486, 3490, 3496, 3500, 3506, 3510, 3514, 3518,
+ 3522, 3526, 3534, 3541, 3552, 3553, 3557, 3561, 3560, 3577,
+ 3578, 3581, 3587, 3605, 3625, 3626, 3632, 3638, 3644, 3651,
+ 3656, 3663, 3667, 3673, 3677, 3683, 3684, 3687, 3691, 3697,
+ 3701, 3705, 3709, 3715, 3720, 3725, 3729, 3733, 3737, 3741,
+ 3745, 3749, 3753, 3757, 3761, 3765, 3769, 3773, 3777, 3782,
+ 3788, 3793, 3798, 3803, 3808, 3815, 3819, 3826, 3831, 3830,
+ 3842, 3846, 3852, 3860, 3868, 3876, 3880, 3886, 3890, 3896,
+ 3897, 3900, 3905, 3912, 3913, 3916, 3922, 3926, 3932, 3937,
+ 3937, 3962, 3963, 3969, 3974, 3980, 3981, 3984, 3990, 3995,
+ 4005, 4012, 4013, 4014, 4017, 4018, 4019, 4020, 4023, 4024,
+ 4025, 4028, 4029, 4032, 4036, 4042, 4043, 4049, 4050, 4053,
+ 4054, 4057, 4060, 4061, 4062, 4065, 4066, 4067, 4070, 4077,
+ 4078, 4082
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 1
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "keyword_class", "keyword_module",
+ "keyword_def", "keyword_begin", "keyword_if", "keyword_unless",
+ "keyword_while", "keyword_until", "keyword_for", "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__", "\"local variable or method\"",
+ "\"method\"", "\"global variable\"", "\"instance variable\"",
+ "\"constant\"", "\"class variable\"", "\"label\"", "\"integer literal\"",
+ "\"float literal\"", "\"character literal\"", "tXSTRING", "tREGEXP",
+ "tSTRING", "tSTRING_PART", "tSTRING_MID", "tNTH_REF", "tBACK_REF",
+ "tREGEXP_END", "\"numbered parameter\"", "\"unary plus\"",
+ "\"unary minus\"", "\"<=>\"", "\"==\"", "\"===\"", "\"!=\"", "\">=\"",
+ "\"<=\"", "\"&&\"", "\"||\"", "\"=~\"", "\"!~\"", "\"..\"", "\"...\"",
+ "tBDOT2", "tBDOT3", "tAREF", "tASET", "\"<<\"", "\">>\"", "\"::\"",
+ "tCOLON3", "tOP_ASGN", "\"=>\"", "tLPAREN", "\"(\"", "\")\"", "\"[\"",
+ "tLBRACE", "\"{\"", "\"*\"", "tPOW", "\"**\"", "\"&\"", "\"->\"",
+ "\"&.\"", "\"symbol\"", "\"string literal\"", "tXSTRING_BEG",
+ "tSTRING_DVAR", "tREGEXP_BEG", "tWORDS_BEG", "tSYMBOLS_BEG", "tLAMBEG",
+ "\"here document\"", "tHEREDOC_END", "tLITERAL_DELIM",
+ "tHD_LITERAL_DELIM", "tHD_STRING_PART", "tHD_STRING_MID", "tLOWEST",
+ "'='", "'?'", "':'", "'>'", "'<'", "'|'", "'^'", "'&'", "'+'", "'-'",
+ "'*'", "'/'", "'%'", "tUMINUS_NUM", "'!'", "'~'", "tLAST_TOKEN", "'{'",
+ "'}'", "'['", "']'", "','", "'`'", "'('", "')'", "';'", "'.'", "'\\n'",
+ "$accept", "program", "$@1", "top_compstmt", "top_stmts", "top_stmt",
+ "@2", "bodystmt", "compstmt", "stmts", "stmt", "$@3", "command_asgn",
+ "command_rhs", "expr", "defn_head", "defs_head", "$@4", "expr_value",
+ "command_call", "block_command", "cmd_brace_block", "$@5", "command",
+ "mlhs", "mlhs_inner", "mlhs_basic", "mlhs_item", "mlhs_list",
+ "mlhs_post", "mlhs_node", "lhs", "cname", "cpath", "fname", "fsym",
+ "undef_list", "$@6", "op", "reswords", "arg", "aref_args", "arg_rhs",
+ "paren_args", "opt_paren_args", "opt_call_args", "call_args",
+ "command_args", "@7", "block_arg", "opt_block_arg", "comma", "args",
+ "mrhs", "primary", "@8", "@9", "$@10", "$@11", "@12", "@13", "$@14",
+ "$@15", "$@16", "$@17", "$@18", "$@19", "@20", "@21", "@22", "@23",
+ "primary_value", "then", "do", "if_tail", "opt_else", "for_var",
+ "f_margs", "$@24", "block_args_tail", "opt_block_args_tail",
+ "block_param", "opt_block_param", "block_param_def", "$@25",
+ "opt_bv_decl", "bv_decls", "bvar", "f_larglist", "lambda_body",
+ "do_block", "$@26", "block_call", "method_call", "brace_block", "@27",
+ "@28", "case_body", "cases", "opt_rescue", "exc_list", "exc_var",
+ "opt_ensure", "literal", "string", "string_fragment", "string_rep",
+ "string_interp", "@29", "xstring", "regexp", "heredoc", "heredoc_bodies",
+ "heredoc_body", "heredoc_string_rep", "heredoc_string_interp", "@30",
+ "words", "symbol", "basic_symbol", "sym", "symbols", "numeric",
+ "variable", "var_lhs", "var_ref", "backref", "superclass", "$@31",
+ "f_opt_arglist_paren", "f_arglist_paren", "f_arglist", "f_label", "f_kw",
+ "f_block_kw", "f_block_kwarg", "f_kwarg", "kwrest_mark", "f_kwrest",
+ "args_tail", "opt_args_tail", "f_args", "f_bad_arg", "f_norm_arg",
+ "f_arg_item", "@32", "f_arg", "f_opt_asgn", "f_opt", "f_block_opt",
+ "f_block_optarg", "f_optarg", "restarg_mark", "f_rest_arg",
+ "blkarg_mark", "f_block_arg", "opt_f_block_arg", "singleton", "$@33",
+ "assoc_list", "assocs", "label_tag", "assoc", "operation", "operation2",
+ "operation3", "dot_or_colon", "call_op", "call_op2", "opt_terms",
+ "opt_nl", "rparen", "trailer", "term", "nl", "terms", "none", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+ (internal) symbol number NUM (which must be that of a token). */
+static const yytype_int16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+ 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
+ 375, 61, 63, 58, 62, 60, 124, 94, 38, 43,
+ 45, 42, 47, 37, 376, 33, 126, 377, 123, 125,
+ 91, 93, 44, 96, 40, 41, 59, 46, 10
+};
+# endif
+
+#define YYPACT_NINF (-850)
+
+#define yypact_value_is_default(Yyn) \
+ ((Yyn) == YYPACT_NINF)
+
+#define YYTABLE_NINF (-612)
+
+#define yytable_value_is_error(Yyn) \
+ ((Yyn) == YYTABLE_NINF)
+
+ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+static const yytype_int16 yypact[] =
+{
+ -850, 124, 3596, -850, 8329, 10453, 10795, 6637, -850, 10099,
+ 10099, -850, -850, 10567, 7819, 6372, 8565, 8565, -850, -850,
+ 8565, 4253, 3845, -850, -850, -850, -850, 200, 7819, -850,
+ 37, -850, -850, -850, 6779, 3709, -850, -850, 6921, -850,
+ -850, -850, -850, -850, -850, -850, 34, 10217, 10217, 10217,
+ 10217, 106, 5631, 633, 9037, 9391, 8101, -850, 7537, 1187,
+ 767, 989, 1214, 1237, -850, 86, 10335, 10217, -850, 751,
+ -850, 1505, -850, 115, 1288, 1288, -850, -850, 176, 70,
+ -850, 71, 10681, -850, 118, 12995, 477, 483, 60, 84,
+ -850, 365, -850, -850, -850, -850, -850, -850, -850, -850,
+ -850, 349, 165, -850, 501, 82, -850, -850, -850, -850,
+ -850, 131, 131, 137, 506, 1044, -850, 10099, 314, 5750,
+ 268, 1266, 1266, -850, 143, -850, 599, -850, -850, 82,
+ -850, -850, -850, -850, -850, -850, -850, -850, -850, -850,
+ -850, -850, -850, -850, -850, -850, -850, -850, -850, -850,
+ -850, -850, -850, -850, -850, -850, -850, -850, 49, 58,
+ 72, 83, -850, -850, -850, -850, -850, -850, 133, 162,
+ 187, 196, -850, 208, -850, -850, -850, -850, -850, -850,
+ -850, -850, -850, -850, -850, -850, -850, -850, -850, -850,
+ -850, -850, -850, -850, -850, -850, -850, -850, -850, -850,
+ -850, -850, -850, -850, -850, -850, -850, -850, -850, 241,
+ 4809, 233, 115, 1288, 1288, 69, 169, 13119, 628, 151,
+ 203, 155, 69, 10099, 10099, 659, 247, -850, -850, 732,
+ 329, 67, 87, -850, -850, -850, -850, -850, -850, -850,
+ -850, -850, 7678, -850, -850, 228, -850, -850, -850, -850,
+ -850, -850, 751, -850, 721, -850, 352, -850, -850, 751,
+ 3981, 10217, 10217, 10217, 10217, -850, 13057, -850, -850, 237,
+ 324, 237, -850, -850, -850, 8683, -850, -850, -850, 8565,
+ -850, -850, -850, 6372, 10099, -850, -850, 251, 5869, -850,
+ 851, 286, 13181, 13181, 544, 8447, 5631, 275, 751, 1505,
+ 751, 303, -850, 8447, 751, 292, 1351, 1351, -850, 13057,
+ 288, 1351, -850, 381, 10909, 296, 862, 905, 929, 1387,
+ -850, -850, -850, -850, 1282, -850, -850, -850, -850, -850,
+ -850, 814, 1287, -850, -850, 1021, -850, 1052, -850, 1320,
+ -850, 1337, 341, 350, -850, -850, -850, -850, 6134, 10099,
+ 10099, 10099, 10099, 8447, 10099, 10099, 61, -850, -850, -850,
+ -850, -850, -850, -850, -850, -850, -850, -850, -850, 1604,
+ 337, 340, 4809, 10217, -850, 321, 417, 354, -850, 751,
+ -850, -850, -850, 387, 10217, -850, 401, 472, 403, 511,
+ -850, -850, 449, 4809, -850, -850, 9509, -850, 5631, 8215,
+ 441, 9509, 10217, 10217, 10217, 10217, 10217, 10217, 10217, 10217,
+ 10217, 10217, 10217, 10217, 10217, 10217, 534, 10217, 10217, 10217,
+ 10217, 10217, 10217, 10217, 10217, 10217, 10217, 10217, 10217, 3391,
+ -850, 8565, -850, 11187, -850, -850, 12391, -850, -850, -850,
+ -850, 10335, 10335, -850, 491, -850, 115, -850, 952, -850,
+ -850, -850, -850, -850, -850, 11273, 8565, 11359, 4809, 10099,
+ -850, -850, -850, 577, 581, 158, 474, 479, -850, 4955,
+ 582, 10217, 11445, 8565, 11531, 10217, 10217, 5247, 639, 639,
+ 94, 11617, 8565, 11703, -850, 541, -850, 5869, 352, -850,
+ -850, 9627, 600, -850, 814, 10217, 13119, 13119, 13119, 10217,
+ 1057, -850, 8801, -850, 10217, -850, 9155, 6491, 471, 751,
+ 237, 237, -850, -850, 885, 473, -850, -850, 7819, 5366,
+ 498, 11445, 11531, 10217, 1505, 751, -850, -850, 6253, 484,
+ 1505, -850, -850, 9273, -850, 751, 9391, -850, -850, -850,
+ 952, 71, 10909, -850, 10909, 11789, 8565, 11875, 1458, -850,
+ -850, -850, 1348, 5869, 814, -850, -850, -850, -850, -850,
+ -850, -850, 10217, 10217, -850, -850, -850, -850, -850, -850,
+ -850, -850, -850, -850, 1165, 751, 751, 497, 10335, 632,
+ 13119, 345, -850, -850, -850, 12, -850, -850, 1458, -850,
+ 13119, 1458, -850, -850, 1769, -850, -850, 10335, 644, 63,
+ 10217, -850, 12711, 237, -850, 751, 10909, 512, -850, -850,
+ -850, 604, 542, 2903, -850, -850, 956, 284, 2755, 2755,
+ 2755, 2755, 1752, 1752, 3456, 2669, 2755, 2755, 13181, 13181,
+ 1232, 1232, -850, 286, 12649, 1752, 1752, 1467, 1467, 1477,
+ 355, 355, 286, 286, 286, 3275, 7277, 4525, 7395, -850,
+ 131, -850, 523, 237, 564, -850, 588, -850, -850, 4117,
+ -850, -850, 2330, 63, 63, -850, 12463, -850, -850, -850,
+ -850, -850, 751, 10099, 4809, 1109, 192, -850, 131, 529,
+ 131, 658, 885, 7960, -850, 9745, 657, -850, 10217, 10217,
+ 487, -850, 7039, 7158, 546, 311, 338, 657, -850, -850,
+ -850, -850, 91, 98, 556, 103, 104, 10099, 7819, 555,
+ 685, 13119, 249, -850, 814, 13119, 13119, 814, 10217, 13057,
+ -850, 237, 13119, -850, -850, -850, -850, 8919, 9155, -850,
+ -850, -850, 568, -850, -850, 38, 1505, 751, 1351, 441,
+ -850, 1109, 192, 562, 1167, 1168, 565, 77, -850, 573,
+ -850, 286, 286, -850, 147, 751, 572, -850, -850, 1708,
+ 673, 12525, -850, 665, -850, 354, -850, -850, -850, 587,
+ 590, 593, -850, 596, 665, 593, 679, 12587, -850, -850,
+ 1458, 4809, -850, -850, 12782, 9863, -850, -850, 10909, 8447,
+ 10335, 10217, 11961, 8565, 12047, 76, 10335, 10335, -850, 491,
+ 613, 8801, 10335, 10335, -850, 491, 84, 176, 4809, 5869,
+ 63, -850, 751, 726, -850, -850, -850, -850, 12711, -850,
+ 652, -850, 5512, 742, -850, 10099, 747, -850, 10217, 10217,
+ 386, 10217, 10217, 750, 6015, 6015, 107, 639, -850, -850,
+ -850, 9981, 5101, 814, 13119, -850, 6491, 237, -850, -850,
+ -850, 756, 624, 640, 4809, 5869, -850, -850, -850, 646,
+ -850, 1485, 751, 10217, 10217, -850, 1458, -850, 1769, -850,
+ 1769, -850, 1769, -850, -850, 10217, 10217, -850, 565, 565,
+ 11023, -850, 650, 354, 653, 11023, -850, 660, 661, -850,
+ 755, 10217, 12853, -850, -850, 13119, 4389, 4661, 666, 414,
+ 426, 10217, 10217, -850, -850, -850, -850, -850, 10335, -850,
+ -850, -850, -850, -850, -850, -850, 759, 670, 5869, 4809,
+ -850, -850, 11137, 69, -850, -850, 6015, -850, -850, 69,
+ -850, 10217, -850, 785, 797, -850, 13119, 148, -850, 9155,
+ -850, 1683, 799, 676, 1630, 1630, 894, -850, 13119, 13119,
+ 593, 681, 593, 593, 13119, 13119, 698, 712, 786, 957,
+ 345, -850, -850, 1422, -850, 957, 1458, -850, 1769, -850,
+ -850, 12924, 486, 13119, 13119, -850, -850, -850, -850, 705,
+ 832, 796, -850, 968, 905, 929, 4809, -850, 4955, -850,
+ -850, 6015, -850, -850, -850, -850, 116, -850, -850, -850,
+ -850, 713, 713, 1630, 714, -850, 1769, -850, -850, -850,
+ -850, -850, -850, 12133, -850, 354, 345, -850, -850, 716,
+ 718, 722, -850, 723, 722, -850, -850, 952, 12219, 8565,
+ 12305, 581, 487, 870, 1683, -850, 1630, 713, 1630, 593,
+ 733, 743, -850, 1458, -850, 1769, -850, 1769, -850, 1769,
+ -850, -850, 1109, 192, 745, 553, 866, -850, -850, -850,
+ -850, 713, -850, 722, 753, 722, 722, 756, -850, 1769,
+ -850, -850, -850, 722, -850
+};
+
+ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE does not specify something else to do. Zero
+ means the default is an error. */
+static const yytype_int16 yydefact[] =
+{
+ 2, 0, 0, 1, 0, 0, 0, 0, 293, 0,
+ 0, 317, 320, 0, 0, 597, 337, 338, 339, 340,
+ 305, 270, 270, 488, 487, 489, 490, 599, 0, 10,
+ 0, 492, 491, 493, 479, 583, 481, 480, 483, 482,
+ 475, 476, 437, 438, 494, 495, 291, 0, 0, 0,
+ 0, 0, 0, 295, 611, 611, 88, 312, 0, 0,
+ 0, 0, 0, 0, 452, 0, 0, 0, 3, 597,
+ 6, 9, 27, 33, 539, 539, 49, 60, 59, 0,
+ 76, 0, 80, 90, 0, 54, 248, 0, 61, 310,
+ 284, 285, 435, 286, 287, 288, 433, 432, 464, 434,
+ 431, 486, 0, 289, 290, 270, 5, 8, 337, 338,
+ 305, 611, 413, 0, 113, 114, 291, 0, 0, 0,
+ 0, 539, 539, 116, 496, 341, 0, 486, 290, 0,
+ 333, 168, 178, 169, 165, 194, 195, 196, 197, 176,
+ 191, 184, 174, 173, 189, 172, 171, 167, 192, 166,
+ 179, 183, 185, 177, 170, 186, 193, 188, 187, 180,
+ 190, 175, 164, 182, 181, 163, 161, 162, 158, 159,
+ 160, 118, 120, 119, 153, 154, 131, 132, 133, 140,
+ 137, 139, 134, 135, 155, 156, 141, 142, 146, 149,
+ 150, 136, 138, 128, 129, 130, 143, 144, 145, 147,
+ 148, 151, 152, 157, 569, 55, 121, 122, 568, 0,
+ 0, 0, 58, 539, 539, 0, 0, 54, 0, 486,
+ 0, 290, 0, 0, 0, 112, 0, 352, 351, 0,
+ 0, 486, 290, 187, 180, 190, 175, 158, 159, 160,
+ 118, 119, 0, 123, 125, 20, 124, 455, 460, 459,
+ 605, 608, 597, 607, 0, 457, 0, 609, 606, 598,
+ 581, 0, 0, 0, 0, 265, 277, 74, 269, 611,
+ 435, 611, 573, 75, 73, 611, 259, 306, 72, 0,
+ 258, 412, 71, 597, 0, 600, 18, 0, 0, 221,
+ 0, 222, 209, 212, 302, 0, 0, 0, 597, 15,
+ 597, 78, 14, 0, 597, 0, 602, 602, 249, 0,
+ 0, 602, 571, 0, 0, 86, 0, 96, 103, 539,
+ 469, 468, 470, 471, 0, 467, 466, 439, 444, 443,
+ 446, 0, 0, 441, 448, 0, 450, 0, 462, 0,
+ 473, 0, 477, 478, 53, 236, 237, 4, 598, 0,
+ 0, 0, 0, 0, 0, 0, 546, 542, 541, 540,
+ 543, 544, 548, 560, 515, 516, 564, 563, 559, 539,
+ 0, 504, 0, 508, 513, 611, 518, 611, 538, 0,
+ 545, 547, 550, 524, 0, 557, 524, 562, 524, 0,
+ 522, 500, 0, 0, 400, 402, 0, 92, 0, 84,
+ 81, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 208, 211, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 594, 611, 593, 0, 596, 595, 0, 417, 415, 311,
+ 436, 0, 0, 406, 65, 309, 330, 113, 114, 115,
+ 477, 478, 504, 497, 328, 0, 611, 0, 0, 0,
+ 592, 591, 56, 0, 611, 302, 0, 0, 343, 0,
+ 342, 0, 0, 611, 0, 0, 0, 0, 0, 0,
+ 302, 0, 611, 0, 325, 0, 126, 0, 0, 456,
+ 458, 0, 0, 610, 575, 0, 278, 580, 272, 0,
+ 275, 266, 0, 274, 0, 267, 0, 597, 0, 597,
+ 611, 611, 260, 271, 597, 0, 308, 52, 0, 0,
+ 0, 0, 0, 0, 17, 597, 300, 13, 598, 77,
+ 296, 299, 303, 604, 250, 603, 604, 252, 304, 572,
+ 102, 94, 0, 89, 0, 0, 611, 0, 539, 313,
+ 397, 472, 0, 0, 447, 453, 440, 442, 449, 451,
+ 463, 474, 0, 0, 7, 21, 22, 23, 24, 25,
+ 50, 51, 506, 552, 0, 597, 597, 524, 0, 0,
+ 507, 0, 520, 567, 517, 0, 521, 505, 0, 531,
+ 553, 0, 534, 561, 0, 536, 565, 0, 0, 611,
+ 0, 28, 30, 0, 31, 597, 0, 82, 93, 48,
+ 34, 46, 0, 253, 198, 29, 0, 290, 226, 231,
+ 232, 233, 228, 230, 240, 241, 234, 235, 207, 210,
+ 238, 239, 32, 218, 599, 227, 229, 223, 224, 225,
+ 213, 214, 215, 216, 217, 584, 589, 585, 590, 411,
+ 270, 409, 0, 611, 584, 586, 585, 587, 410, 270,
+ 584, 585, 270, 611, 611, 35, 253, 199, 45, 206,
+ 63, 66, 0, 0, 0, 113, 114, 117, 0, 0,
+ 611, 0, 597, 0, 294, 611, 611, 423, 0, 0,
+ 611, 344, 588, 301, 0, 584, 585, 611, 346, 318,
+ 345, 321, 588, 301, 0, 584, 585, 0, 0, 0,
+ 0, 277, 0, 324, 576, 578, 577, 0, 0, 279,
+ 273, 611, 579, 574, 257, 255, 261, 262, 264, 307,
+ 601, 19, 0, 26, 205, 79, 16, 597, 602, 95,
+ 87, 99, 101, 0, 98, 100, 599, 0, 465, 0,
+ 454, 219, 220, 546, 360, 597, 353, 503, 501, 0,
+ 41, 244, 335, 0, 514, 611, 566, 523, 551, 524,
+ 524, 524, 558, 524, 546, 524, 43, 246, 336, 388,
+ 386, 0, 385, 384, 283, 0, 91, 85, 0, 0,
+ 0, 0, 0, 611, 0, 0, 0, 0, 408, 69,
+ 414, 262, 0, 0, 407, 67, 403, 62, 0, 0,
+ 611, 331, 0, 0, 414, 334, 570, 57, 424, 425,
+ 611, 426, 0, 611, 349, 0, 0, 347, 0, 0,
+ 414, 0, 0, 0, 0, 0, 414, 0, 127, 461,
+ 323, 0, 0, 276, 280, 268, 597, 611, 11, 297,
+ 251, 97, 0, 390, 0, 0, 314, 445, 361, 358,
+ 549, 0, 597, 0, 0, 519, 0, 527, 0, 529,
+ 0, 535, 0, 532, 537, 0, 0, 383, 599, 599,
+ 510, 511, 611, 611, 368, 0, 555, 368, 368, 366,
+ 0, 0, 281, 83, 47, 254, 584, 585, 0, 584,
+ 585, 0, 0, 40, 203, 39, 204, 70, 0, 37,
+ 201, 38, 202, 68, 404, 405, 0, 0, 0, 0,
+ 498, 329, 0, 0, 428, 350, 0, 12, 430, 0,
+ 315, 0, 316, 0, 0, 326, 279, 611, 256, 263,
+ 396, 0, 0, 0, 0, 0, 356, 502, 42, 245,
+ 524, 524, 524, 524, 44, 247, 0, 0, 0, 509,
+ 0, 364, 365, 368, 376, 554, 0, 379, 0, 381,
+ 401, 282, 414, 243, 242, 36, 200, 418, 416, 0,
+ 0, 0, 427, 0, 104, 111, 0, 429, 0, 319,
+ 322, 0, 420, 421, 419, 394, 599, 392, 395, 399,
+ 398, 362, 359, 0, 354, 528, 0, 525, 530, 533,
+ 389, 387, 302, 0, 512, 611, 0, 367, 374, 368,
+ 368, 368, 556, 368, 368, 64, 332, 110, 0, 611,
+ 0, 611, 611, 0, 0, 391, 0, 357, 0, 524,
+ 588, 301, 363, 0, 371, 0, 373, 0, 380, 0,
+ 377, 382, 107, 109, 0, 584, 585, 422, 348, 327,
+ 393, 355, 526, 368, 368, 368, 368, 105, 372, 0,
+ 369, 375, 378, 368, 370
+};
+
+ /* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -850, -850, -850, 379, -850, 44, -850, -221, 102, -850,
+ 30, -850, -19, -171, 161, 1373, 1745, -850, 1, -30,
+ -850, -656, -850, -13, 889, -220, 23, -60, -285, -450,
+ 15, 2261, -78, 901, 43, 3, -850, -850, 19, -850,
+ 1221, -850, 464, 81, -495, -377, 114, 17, -850, -415,
+ -256, -111, 39, -338, 32, -850, -850, -850, -850, -850,
+ -850, -850, -850, -850, -850, -850, -850, -850, -850, -850,
+ -850, 8, -213, -435, -122, -602, -850, -850, -850, 134,
+ 101, -850, -566, -850, -850, -359, -850, -116, -850, -850,
+ 135, -850, -850, -850, -81, -850, -850, -458, -850, -112,
+ -850, -850, -850, -850, -850, 66, 6, -158, -850, -850,
+ -850, -850, -394, -280, -850, 671, -850, -850, -850, 13,
+ -850, -850, -850, 2384, 2648, 924, 1983, -850, -850, 14,
+ 459, 24, 163, 357, -14, -850, -850, -850, 112, -453,
+ 189, -206, -837, -693, -553, -850, 280, -715, -542, -849,
+ -12, -518, -850, -90, -850, 111, -357, -850, -850, -850,
+ 26, 691, -399, 625, -173, -850, -850, -82, -850, 42,
+ -25, 525, -249, 78, -26, 9, -2
+};
+
+ /* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 1, 2, 68, 69, 70, 287, 463, 464, 298,
+ 299, 518, 72, 610, 73, 213, 214, 683, 215, 76,
+ 77, 671, 810, 78, 79, 300, 80, 81, 82, 543,
+ 83, 216, 123, 124, 243, 244, 245, 708, 648, 207,
+ 85, 305, 614, 649, 277, 508, 509, 278, 279, 268,
+ 501, 536, 653, 604, 86, 210, 303, 737, 304, 319,
+ 747, 223, 834, 224, 835, 707, 991, 674, 672, 919,
+ 458, 290, 469, 699, 826, 827, 230, 755, 944, 1017,
+ 964, 878, 781, 782, 879, 852, 996, 997, 549, 856,
+ 395, 599, 88, 89, 445, 664, 663, 492, 994, 686,
+ 820, 923, 927, 90, 91, 92, 332, 333, 553, 93,
+ 94, 95, 554, 253, 254, 255, 487, 96, 97, 98,
+ 326, 99, 100, 219, 220, 103, 221, 454, 673, 370,
+ 371, 372, 373, 374, 881, 882, 375, 376, 377, 378,
+ 589, 379, 380, 381, 382, 574, 383, 384, 385, 886,
+ 887, 386, 387, 388, 389, 390, 582, 209, 459, 310,
+ 511, 495, 272, 129, 678, 651, 462, 457, 436, 515,
+ 853, 516, 534, 257, 258, 259, 302
+};
+
+ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule whose
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+static const yytype_int16 yytable[] =
+{
+ 106, 285, 284, 265, 265, 433, 435, 265, 439, 477,
+ 87, 222, 87, 126, 126, 505, 252, 218, 218, 280,
+ 586, 229, 400, 218, 218, 218, 206, 246, 218, 542,
+ 710, 286, 71, 206, 71, 768, 344, 125, 125, 282,
+ 449, 246, 271, 271, 701, 125, 271, 206, 107, 772,
+ 205, 555, 308, 312, 652, 269, 269, 256, 537, 269,
+ 87, 859, 539, 615, 316, 885, 335, 337, 339, 341,
+ 769, 315, 391, 391, 218, 301, 525, 206, 348, 679,
+ 307, 311, 270, 270, 823, 468, 270, 720, 125, 392,
+ 316, 720, -104, 306, 740, 833, 694, 808, 809, 393,
+ 714, 325, 276, 281, 998, 704, 854, 723, 437, 280,
+ 437, 347, -111, 550, 125, 366, -107, 1022, 572, -110,
+ 270, 270, 444, -109, 3, 218, -485, 87, -106, -108,
+ 267, 273, -105, 901, 274, 767, 474, 723, 767, -488,
+ 367, 767, 779, 907, 342, 343, 393, 483, -487, 913,
+ 434, 579, 354, 355, 297, -112, 787, 440, 502, -77,
+ 506, 294, -489, 576, 804, 430, 822, 806, 491, 743,
+ 212, 212, 598, -490, 557, 288, 212, 557, 605, 557,
+ -91, 557, 573, 557, 247, 804, 443, 248, 249, 780,
+ 855, 396, 276, 281, 1022, 533, -488, 998, 753, 902,
+ 357, 358, 359, 360, 394, -487, 768, 432, 522, -96,
+ 443, 391, 391, 397, 555, 250, 361, 251, 87, -489,
+ 438, 297, 438, -492, 478, 479, 275, 466, 467, -103,
+ -490, 218, 218, -99, 547, -584, -102, 681, 393, 401,
+ -101, 769, -585, -484, 918, -98, -100, 476, 885, -97,
+ 523, 885, -491, 1004, 541, 505, 650, 441, 1034, 542,
+ 659, 206, 265, 662, 251, 468, 265, 503, 453, 503,
+ 665, 668, -104, 512, 750, 275, -111, -493, 446, -110,
+ -492, 283, -301, 218, 680, 325, -479, 218, 465, 858,
+ 471, 218, 218, 470, 488, 475, 87, -301, -483, 650,
+ 470, 659, 480, 87, 87, 271, 767, 528, 768, -491,
+ 680, 87, 720, 720, 510, 535, 535, 768, 269, 301,
+ 535, 542, 316, 843, 772, 524, 450, 451, 885, 723,
+ 552, 460, -301, 530, -493, 992, -585, 493, 893, -301,
+ 527, 270, 746, -479, 283, 270, 125, 819, 251, 680,
+ 565, 566, 567, 568, 484, -483, 87, 218, 218, 218,
+ 218, 87, 218, 218, 247, 447, 601, 248, 249, 448,
+ 486, 611, 491, 583, 680, 583, 442, 677, 71, 500,
+ 87, 494, 609, 569, 212, 212, 720, 417, 461, 519,
+ 520, 500, 564, 513, 557, 250, 763, 251, 297, 727,
+ 728, 87, 935, 831, 218, -111, 87, 316, 865, 616,
+ 768, 611, 611, 767, 607, 767, 898, 767, 265, 767,
+ 526, 301, 609, 609, -76, 42, 493, 538, 43, 512,
+ 832, 125, -106, 532, 750, 603, 540, 555, 544, 218,
+ 603, -484, 562, 265, 514, 517, 364, 365, 366, 616,
+ 616, 563, 657, 813, 512, 657, 417, 587, 578, -108,
+ 265, -499, 687, 581, 218, 845, 87, 218, 584, 265,
+ -104, 512, 59, 367, 285, 717, 657, 87, 931, 993,
+ 512, 218, 739, 768, 541, 87, 426, 427, 428, 850,
+ 218, -96, 785, 657, 768, 87, 585, 270, 770, 842,
+ 297, 773, 657, 542, 825, 822, 802, -105, 503, 503,
+ 212, 212, 212, 212, 658, 570, 571, 106, 803, 956,
+ 957, 731, 270, 593, 720, 961, 962, 87, 721, 588,
+ 712, 246, -581, 265, 794, -106, 87, 206, 658, 270,
+ 723, 657, 801, 591, 512, 594, 541, -108, 270, 71,
+ 316, 726, 316, 767, 218, 658, 700, 700, 736, 738,
+ 890, 87, 596, 750, 658, 760, 657, -341, 270, -106,
+ 597, 690, 270, 429, 125, 592, 125, 595, 908, 697,
+ 452, 452, -341, 608, 776, 632, 218, 916, 430, 709,
+ 670, 845, 684, 442, 685, 688, -479, 783, 691, 270,
+ 689, 841, 270, 658, 551, 218, 493, -105, 285, 795,
+ 506, -479, 270, 493, 316, 713, 725, -341, 730, 650,
+ 682, 659, -111, 431, -341, 903, -91, 105, 658, 105,
+ 432, 909, 911, 942, 105, 105, 523, 733, 125, 759,
+ 105, 105, 105, -103, -581, 105, -479, 762, 789, 577,
+ -581, 503, 1054, -479, 788, 749, 802, 280, 1042, 778,
+ 280, 783, 783, 790, 800, -110, 698, 799, -106, 770,
+ 814, -106, -106, 815, 812, 822, 805, 105, 280, 807,
+ 803, 218, 87, 821, 824, -106, -102, 830, 824, 455,
+ 888, 105, 766, 765, 839, 824, 766, 836, 980, -106,
+ 840, -106, 206, 851, 430, 908, -98, 848, 837, -108,
+ 986, 838, 857, 251, 861, 218, 988, 863, 472, 503,
+ 285, 246, 572, 875, 603, 914, 817, 206, 541, 866,
+ -100, 798, 868, 430, -105, 870, 939, 975, 872, 456,
+ 276, 921, 105, 276, 105, 922, 432, 535, -298, -291,
+ 811, -298, -298, 847, 247, -97, 926, 248, 249, 798,
+ 611, 276, 930, 583, -291, 932, 611, 905, 473, 940,
+ 970, 609, 611, 611, 977, 432, 951, 609, -298, -298,
+ 265, -298, -414, 609, 609, 250, 941, 251, 945, 87,
+ 470, 512, 960, 270, 270, 963, 316, 87, 616, -291,
+ 989, 218, 966, 968, 616, 218, -291, 972, 783, 978,
+ 616, 616, 990, 657, 999, 1000, 87, 87, 924, 894,
+ 125, 928, 481, 1006, 1010, 529, 929, 847, 334, 531,
+ 87, 328, 329, 218, 212, 105, 489, 430, 1011, 248,
+ 249, 1012, 87, 87, 1025, 503, -414, 1026, 105, 105,
+ 87, 1027, 285, 285, 756, 1036, 1038, 680, 1043, 270,
+ 1045, -414, 87, 87, 1047, 1049, 247, 270, 212, 248,
+ 249, 771, 482, 1020, 775, 658, 1023, -584, 611, 432,
+ 583, 583, -108, 330, 331, 1059, 1067, -585, 959, 609,
+ 920, 889, 883, 965, -414, 1069, -414, 250, 732, 251,
+ 105, 1030, 227, -414, 105, 667, 669, 130, 105, 105,
+ 1058, 917, 125, 105, 877, 700, 616, 125, 1060, 1057,
+ 105, 105, 354, 355, 925, 490, 87, 87, 105, 247,
+ 983, 208, 248, 249, 87, 824, 933, 934, 764, 667,
+ 669, 521, 915, 880, 937, 753, 1014, 357, 358, 359,
+ 360, 1019, 545, 1064, 125, 0, 430, 943, 867, 869,
+ 871, 504, 873, 361, 874, 0, 0, 430, 0, 0,
+ 285, 1035, 0, 105, 105, 105, 105, 105, 105, 105,
+ 105, -108, 0, 0, -108, -108, 212, 734, 967, 969,
+ 0, 473, 0, 0, 87, -486, 87, 105, 432, 87,
+ 247, 470, 546, 248, 249, 270, 0, 470, 0, 432,
+ -486, 0, -108, 583, -108, 0, 265, 0, 105, -290,
+ 979, 105, 0, 105, 0, 0, 105, 512, 987, 687,
+ 824, 250, 724, 251, -290, 0, 1003, 218, 0, 729,
+ 0, 0, -302, 0, 0, -486, 792, 1013, 0, 657,
+ 735, 336, -486, 328, 329, 0, 105, -302, 1028, 0,
+ 884, 430, 430, 0, 1018, 0, 105, 105, 0, -290,
+ -582, 766, 1015, 430, 889, 883, -290, 889, 883, 889,
+ 883, 105, 558, 105, 105, 328, 329, 0, 1031, 0,
+ 1032, 0, -302, 1033, 105, 270, 793, 456, 105, -302,
+ 757, 758, 105, 432, 432, 330, 331, 105, 1029, 0,
+ 0, 658, 105, 0, 559, 432, 328, 329, 0, 0,
+ 1044, 1046, 1048, 880, 1050, 1051, 880, 889, 883, 880,
+ 786, 880, 0, 0, -483, -588, 0, 330, 331, 1005,
+ 1007, 1008, 1009, 0, 105, 0, 950, 0, 952, -483,
+ 0, 0, 953, 105, 889, 883, 889, 883, 889, 883,
+ 889, 883, 0, 0, 1068, 1070, 1071, 1072, 330, 331,
+ 0, 105, -599, 0, 1074, -599, -599, 0, 105, 880,
+ 889, 883, -582, 0, -483, 0, 0, 0, -582, 0,
+ 0, -483, 0, -584, -585, 0, 0, 0, 0, -588,
+ 0, 0, 0, 105, 0, 251, 880, 816, 880, 0,
+ 880, 0, 880, 0, -588, 0, 753, 0, 357, 358,
+ 359, 360, 105, 0, 1001, 1002, 0, 0, 1062, 0,
+ 217, 217, 880, 0, 361, 0, 217, 266, 266, 0,
+ 0, 266, 0, 0, 0, 0, 1021, -588, 1024, -588,
+ 327, 328, 329, -584, 0, 0, -588, -584, -585, 362,
+ 904, 906, 849, 0, 0, 754, 910, 912, 289, 291,
+ 292, 293, -584, -585, 0, 266, 309, 338, 328, 329,
+ 860, 0, 0, 1037, 0, 0, 1039, 345, 346, 0,
+ 0, 0, 904, 906, 0, 910, 912, 0, 105, 105,
+ 340, 328, 329, 330, 331, -584, -585, -584, -585, 0,
+ 0, -584, -585, 0, -584, -585, 0, 356, 1061, 357,
+ 358, 359, 360, 1063, 0, 1065, 0, 0, 0, 1066,
+ 330, 331, 105, 417, 0, 361, 0, 0, 217, 356,
+ 0, 357, 358, 359, 360, 551, 328, 329, 0, 1073,
+ 556, 328, 329, 330, 331, 0, 0, 361, 0, 0,
+ 362, 424, 425, 426, 427, 428, 363, 364, 365, 366,
+ 0, 938, 976, 0, 0, 74, 0, 74, 121, 121,
+ 0, 0, 362, 560, 328, 329, 121, 947, 363, 364,
+ 365, 366, 0, 0, 367, 976, 0, 368, 330, 331,
+ 561, 328, 329, 330, 331, 0, 105, 0, 0, -611,
+ 369, 748, 328, 329, 105, 105, 367, 0, 105, 368,
+ 0, 105, 105, 0, 0, 74, 0, 105, 105, 121,
+ 0, 0, 369, 105, 105, 0, 330, 331, 356, 0,
+ 357, 358, 359, 360, 217, 217, 0, 105, 0, 0,
+ 105, 0, 0, 330, 331, 121, 361, 0, 0, 105,
+ 105, 0, 0, 0, 330, 331, 247, 105, 0, 248,
+ 249, 0, 0, 356, 0, 357, 358, 359, 360, 105,
+ 105, 362, 496, 497, 498, 345, 0, 363, 364, 365,
+ 366, 361, 74, 500, 0, 0, 266, 250, 0, 251,
+ 266, 0, 0, 0, 217, 217, 0, 0, 0, 356,
+ 0, 357, 358, 359, 360, 367, 362, 0, 368, 0,
+ 0, 0, 363, 364, 365, 366, 0, 361, 0, 0,
+ 0, 548, 0, 105, 0, 0, 753, 0, 357, 358,
+ 359, 360, 0, 105, 105, 349, 350, 351, 352, 353,
+ 367, 105, 362, 368, 361, 414, 415, 0, 363, 364,
+ 365, 366, 0, 0, 1016, 414, 415, 0, 417, 0,
+ 217, 217, 217, 217, 0, 217, 217, 0, 417, 362,
+ 0, 0, 0, 74, 0, 946, 367, 0, 0, 368,
+ 0, 0, 0, 0, 580, 423, 424, 425, 426, 427,
+ 428, 0, 0, 0, 0, 590, 424, 425, 426, 427,
+ 428, 105, 0, 105, 0, 0, 105, 602, 0, 0,
+ 0, 0, 613, 618, 619, 620, 621, 622, 623, 624,
+ 625, 626, 627, 628, 629, 630, 631, 0, 633, 634,
+ 635, 636, 637, 638, 639, 640, 641, 642, 643, 644,
+ 0, 0, 266, 0, 105, 356, 0, 357, 358, 359,
+ 360, 74, 666, 666, 0, 0, 0, 0, 74, 74,
+ 0, 0, 0, 361, 0, 0, 74, 266, 0, 0,
+ 217, 753, 0, 357, 358, 359, 360, 121, 0, 575,
+ 0, 0, 666, 0, 266, 0, 666, 666, 362, 361,
+ 0, 0, 0, 266, 363, 364, 365, 366, 0, 0,
+ 0, 0, 711, 0, 0, 0, 715, 0, 0, 0,
+ 716, 74, 0, 719, 362, 722, 74, 309, 293, 0,
+ 0, 0, 367, 0, 995, 368, 357, 358, 359, 360,
+ 0, 0, 0, 0, 666, 74, 0, 75, 0, 75,
+ 122, 122, 361, 0, 719, 0, 0, 309, 122, 356,
+ 0, 357, 358, 359, 360, 0, 74, 266, 0, 0,
+ 0, 74, 121, 0, 74, 0, 0, 361, 0, 0,
+ 0, 0, 0, 751, 752, 0, 0, 0, 0, 0,
+ 0, 0, 0, 862, 0, 0, 0, 75, 0, 761,
+ 0, 122, 362, 0, 0, 0, 0, 0, 363, 364,
+ 365, 366, 0, 0, 74, 74, 0, 0, 777, 0,
+ 774, 784, 357, 358, 359, 360, 0, 122, 0, 0,
+ 0, 74, 0, 0, 0, 0, 367, 0, 361, 368,
+ 414, 415, 74, 0, 0, 0, 0, 0, 0, 0,
+ 74, 0, 0, 417, 0, 0, 0, 0, 0, 0,
+ 74, 0, 0, 362, 75, 0, 0, 0, 0, 0,
+ 364, 365, 366, 0, 0, 0, 0, 0, 421, 422,
+ 423, 424, 425, 426, 427, 428, 0, 0, 0, 0,
+ 0, 0, 74, 0, 217, 0, 0, 367, 0, 0,
+ 0, 74, 0, 0, 0, 0, 818, 0, 0, 761,
+ 777, 0, 0, 0, 0, 121, 0, 121, 0, 0,
+ 0, 0, 0, 0, 0, 0, 74, 0, 217, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 844,
+ 0, 0, 0, 0, 0, 0, 0, 0, 719, 309,
+ 0, 0, 0, 0, 0, 75, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 121,
+ 0, 0, 0, 0, 0, 104, 0, 104, 128, 128,
+ 0, 0, 0, 0, 0, 0, 232, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 892, 0, 0, 0,
+ 0, 666, 895, 0, 266, 0, 0, 666, 666, 0,
+ 0, 0, 719, 666, 666, 0, 0, 0, 0, 0,
+ 0, 0, 0, 75, 0, 104, 0, 0, 0, 318,
+ 75, 75, 0, 0, 0, 0, 217, 74, 75, 666,
+ 666, 0, 666, 666, 0, 0, 0, 0, 0, 122,
+ 0, 0, 936, 0, 0, 318, 0, 293, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 948, 949, 0, 0, 0, 0,
+ 0, 0, 0, 75, 0, 0, 954, 955, 75, 0,
+ 0, 0, 104, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 971, 0, 0, 0, 0, 75, 0, 0,
+ 0, 0, 973, 974, 0, 0, 0, 0, 0, 666,
+ 0, 0, 0, 0, 0, 0, 0, 0, 75, 0,
+ 0, 0, 0, 75, 122, 0, 75, 0, 0, 0,
+ 0, 0, 666, 0, 74, 0, 0, 0, 0, 0,
+ 309, 121, 74, 74, 0, 0, 0, 0, 0, 74,
+ 0, 0, 0, 0, 0, 74, 74, 0, 0, 0,
+ 0, 74, 74, 0, 0, 0, 75, 75, 0, 0,
+ 0, 0, 0, 104, 0, 74, 0, 0, 0, 0,
+ 0, 0, 0, 75, 0, 0, 0, 74, 74, 0,
+ 0, 0, 0, 0, 75, 74, 0, 0, 0, 0,
+ 0, 0, 75, 0, 0, 0, 0, 74, 74, 0,
+ 0, 0, 75, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 266, 0, 0, 121, 0, 0, 0, 0, 121, 0,
+ 0, 0, 0, 84, 75, 84, 0, 0, 0, 0,
+ 0, 104, 0, 75, 228, 0, 0, 0, 104, 104,
+ 0, 74, 0, 0, 0, 0, 104, 122, 0, 122,
+ 0, 74, 74, 0, 0, 121, 0, 318, 75, 74,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 84, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -611, 104, 0, 0, 0, 0, 104, 0, 0, 0,
+ 0, 0, 0, -611, -611, -611, -611, -611, -611, 0,
+ -611, 122, 0, 0, 0, 104, -611, -611, 0, 74,
+ 0, 74, 0, 0, 74, 0, 0, -611, -611, 0,
+ -611, -611, -611, -611, -611, 0, 104, 0, 0, 0,
+ 84, 104, 318, 0, 617, 0, 101, 0, 101, 127,
+ 127, 127, 0, 0, 0, 0, 0, 231, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 75,
+ -611, 0, 0, 0, 617, 617, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -611, 101, 0, 0, 0,
+ 317, 104, 0, 0, 0, -611, 0, 0, -611, -611,
+ 0, 0, 104, 0, 0, 0, 0, 0, 0, 0,
+ 104, 0, 0, 0, 0, 0, 317, 0, -611, -611,
+ 104, 84, 0, 0, 275, -611, -611, -611, -611, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 104, 101, 0, 0, 0, 0, 0, 0,
+ 0, 104, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 318, 75, 318, 0, 0,
+ 0, 0, 0, 122, 75, 75, 104, 0, 0, 0,
+ 0, 75, 0, 0, 0, 0, 0, 75, 75, 84,
+ 0, 0, 0, 75, 75, 0, 84, 84, 0, 0,
+ 0, 0, 0, 0, 84, 0, 0, 75, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 75,
+ 75, 0, 0, 0, 0, 0, 0, 75, 0, 318,
+ 0, 0, 0, 0, 101, 0, 0, 0, 0, 75,
+ 75, 0, 0, 0, 0, 0, 0, 0, 0, 84,
+ 0, 0, 0, 0, 84, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 122, 0, 0, 0, 0,
+ 122, 0, 0, 84, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 102, 0, 102, 75, 84, 0, 0, 104, 0, 84,
+ 0, 0, 612, 75, 75, 0, 0, 122, 0, 0,
+ 0, 75, 101, 0, 0, 0, 0, 0, 0, 101,
+ 101, 0, 0, 0, 0, 0, 0, 101, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 317, 0,
+ 102, 0, 612, 612, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 84,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 84, 75, 101, 75, 0, 0, 75, 101, 84, 0,
+ 0, 402, 403, 404, 405, 406, 407, 408, 84, 410,
+ 411, 0, 0, 0, 0, 0, 101, 414, 415, 0,
+ 0, 0, 0, 0, 104, 0, 0, 102, 0, 0,
+ 417, 318, 104, 617, 0, 0, 0, 101, 0, 617,
+ 84, 0, 101, 317, 0, 617, 617, 0, 0, 84,
+ 0, 104, 104, 419, 420, 421, 422, 423, 424, 425,
+ 426, 427, 428, 0, 0, 104, 0, 0, 0, 0,
+ 0, 0, 0, 0, 84, 0, 0, 104, 104, 0,
+ 0, 0, 0, 0, 0, 104, 0, -612, -612, -612,
+ -612, 406, 407, 0, 0, -612, -612, 104, 104, 0,
+ 0, 0, 101, 414, 415, 0, 0, 0, 0, 0,
+ 0, 0, 0, 101, 0, 0, 417, 0, 102, 0,
+ 0, 101, 0, 128, 0, 0, 0, 0, 128, 0,
+ 0, 101, 0, 0, 0, 0, 0, 0, 0, 419,
+ 420, 421, 422, 423, 424, 425, 426, 427, 428, 0,
+ 0, 617, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 104, 104, 101, 0, 985, 0, 0, 0, 104,
+ 0, 0, 101, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 317, 0, 317, 0,
+ 0, 0, 0, 0, 0, 84, 102, 101, 0, 0,
+ 0, 0, 0, 102, 102, 0, 0, 791, 0, 0,
+ 0, 102, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 104,
+ 0, 104, 0, 0, 104, 402, 403, 404, 405, 406,
+ 407, 408, 409, 410, 411, 412, 413, 0, 0, 0,
+ 317, 414, 415, 0, 0, 0, 102, 0, 0, 0,
+ 0, 102, 0, 0, 417, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 102, 0, 0, 0, 0, 418, 0, 419, 420, 421,
+ 422, 423, 424, 425, 426, 427, 428, 0, 0, 0,
+ 0, 102, 84, 0, 0, -277, 102, 0, 0, 102,
+ 84, 612, 0, 0, 0, 0, 0, 612, 101, 0,
+ 0, 0, 0, 612, 612, 0, 0, 0, 0, 84,
+ 84, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 84, 0, 0, 0, 0, 0, 102,
+ 102, 0, 0, 0, 0, 84, 84, 0, 0, 0,
+ 0, 0, 0, 84, 0, 0, 102, 0, 0, 0,
+ 0, 0, 0, 0, 0, 84, 84, 102, 0, 0,
+ 0, 0, 0, 0, 0, 102, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 102, 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, 101, 0, 102, 0, 612,
+ 0, 0, 317, 101, 0, 0, 102, 0, 0, 84,
+ 84, 0, 0, 982, 0, 0, 0, 84, 0, 0,
+ 0, 0, 101, 101, 0, 0, 0, 0, 0, 0,
+ 0, 102, 0, 0, 0, 0, 101, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 101, 101,
+ 0, 0, 0, 0, 0, 0, 101, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 101, 101,
+ 0, 0, 0, 0, 0, 0, 0, 84, 0, 84,
+ 0, 0, 84, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 127, 0, 0, 0, 0, 127,
+ 0, 0, 0, 0, 0, -588, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -588, -588,
+ -588, 0, -588, -588, 0, -588, 0, 0, 0, 0,
+ 0, -588, 101, 101, 0, 0, 984, 0, 0, 0,
+ 101, 0, -588, -588, 0, -588, -588, -588, -588, -588,
+ 0, 0, 102, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -588, -588, -588,
+ -588, -588, -588, -588, -588, -588, -588, -588, -588, 0,
+ 0, 0, 0, -588, -588, -588, 0, 796, -588, 0,
+ 101, 0, 101, 0, 0, 101, -588, 0, 0, 0,
+ -588, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -588, 0, 0, -588, -588, 0, -107, -588, 0, -588,
+ -588, -588, -588, -588, -588, -588, -588, -588, -588, 0,
+ 0, 0, 0, -588, -588, -588, 0, -99, 0, 0,
+ -588, -588, -588, -588, 0, 0, 0, 0, 0, 102,
+ 0, 0, 0, 0, 0, 0, 0, 102, 102, 0,
+ 0, 0, 645, 646, 102, 0, 647, 0, 0, 0,
+ 102, 102, 0, 0, 0, 0, 102, 102, 0, 0,
+ 0, 174, 175, 176, 177, 178, 179, 180, 181, 0,
+ 102, 182, 183, 0, 0, 0, 0, 184, 185, 186,
+ 187, 0, 102, 102, 0, 0, 0, 0, 0, 0,
+ 102, 188, 189, 190, 0, 0, 0, 0, 0, 0,
+ 0, 0, 102, 102, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 0, 201, 202, 402, 403,
+ 404, 405, 406, 407, 203, 275, 410, 411, 0, 0,
+ 0, 0, 0, 0, 414, 415, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 102, 417, 0, 0,
+ 0, 0, 0, 0, 0, 0, 102, 102, 0, 0,
+ 0, 0, 0, 0, 102, 0, 0, 0, 0, 0,
+ 419, 420, 421, 422, 423, 424, 425, 426, 427, 428,
+ 0, 0, 0, 0, 0, 0, -611, 4, 0, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 0,
+ 0, 0, 0, 0, 0, 15, 0, 16, 17, 18,
+ 19, 0, 0, 0, 0, 0, 20, 21, 22, 23,
+ 24, 25, 26, 0, 102, 27, 102, 0, 0, 102,
+ 0, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 0, 40, 41, 42, 0, 0, 43,
+ 0, 0, 44, 45, 0, 46, 47, 48, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 49, 50, 0, 0, 0, 0, 0, 51, 0, 0,
+ 52, 53, 0, 54, 55, 0, 56, 0, 0, 0,
+ 57, 0, 58, 59, 60, 0, 61, 62, 63, -292,
+ 64, -611, 0, 0, -611, -611, 0, 0, 0, 0,
+ 0, 0, -292, -292, -292, -292, -292, -292, 0, -292,
+ 65, 66, 67, 0, 0, 0, -292, -292, -292, 0,
+ 0, 0, -611, 0, -611, 0, -292, -292, 0, -292,
+ -292, -292, -292, -292, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -292, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -292, -292, -292, -292, -292, -292, -292, -292, -292,
+ -292, -292, -292, 0, 0, 0, 0, -292, -292, -292,
+ 0, 0, -292, 0, 0, 0, 0, 0, -292, 0,
+ -292, 0, 0, 0, -292, 0, 0, 0, 0, 0,
+ 0, 0, -292, 0, -292, 0, 0, -292, -292, 0,
+ 0, -292, -292, -292, -292, -292, -292, -292, -292, -292,
+ -292, -292, -292, 0, 0, -413, 0, 0, -292, -292,
+ -292, -292, 0, 0, -292, -292, -292, -292, -413, -413,
+ -413, -413, -413, -413, 0, -413, 0, 0, 0, 0,
+ 0, -413, -413, -413, 0, 0, 0, 0, 0, 0,
+ 0, 0, -413, -413, 0, -413, -413, -413, -413, -413,
+ 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, -413, -413, -413,
+ -413, -413, -413, -413, -413, -413, -413, -413, -413, 0,
+ 0, 0, 0, -413, -413, -413, 0, 0, -413, 0,
+ 0, 0, 0, 0, -413, 0, -413, 0, 0, 0,
+ -413, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -413, 0, 0, -413, -413, 0, 0, -413, 0, -413,
+ -413, -413, -413, -413, -413, -413, -413, -413, -413, 0,
+ 0, -479, 0, -413, -413, -413, -413, -413, 0, 275,
+ -413, -413, -413, -413, -479, -479, -479, -479, -479, -479,
+ 0, -479, 0, 0, 0, 0, 0, 0, -479, -479,
+ 0, 0, 0, 0, 0, 0, 0, 0, -479, -479,
+ 0, -479, -479, -479, -479, -479, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 494, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -479, -479, -479, -479, -479, -479, -479,
+ -479, -479, -479, -479, -479, 0, 0, 0, 0, -479,
+ -479, -479, 0, -479, -479, 0, 0, 0, 0, 0,
+ -479, 0, -479, 0, 0, 0, -479, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -479, 0, 0, -479,
+ -479, 0, -479, -479, 0, -479, -479, -479, -479, -479,
+ -479, -479, -479, -479, -479, 0, 0, -611, 0, 0,
+ -479, -479, -479, -479, 0, 0, -479, -479, -479, -479,
+ -611, -611, -611, -611, -611, -611, 0, -611, 0, 0,
+ 0, 0, 0, -611, -611, -611, 0, 0, 0, 0,
+ 0, 0, 0, 0, -611, -611, 0, -611, -611, -611,
+ -611, -611, 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, -611,
+ -611, -611, -611, -611, -611, -611, -611, -611, -611, -611,
+ -611, 0, 0, 0, 0, -611, -611, -611, 0, 0,
+ -611, 0, 0, 0, 0, 0, -611, 0, -611, 0,
+ 0, 0, -611, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -611, 0, 0, -611, -611, 0, 0, -611,
+ 0, -611, -611, -611, -611, -611, -611, -611, -611, -611,
+ -611, 0, 0, -611, 0, -611, -611, -611, -611, -611,
+ 0, 275, -611, -611, -611, -611, -611, -611, -611, -611,
+ -611, -611, 0, -611, 0, 0, 0, 0, 0, 0,
+ -611, -611, 0, 0, 0, 0, 0, 0, 0, 0,
+ -611, -611, 0, -611, -611, -611, -611, -611, 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, -611, -611, -611, -611, -611,
+ -611, -611, -611, -611, -611, -611, -611, 0, 0, 0,
+ 0, -611, -611, -611, 0, 0, -611, 0, 0, 0,
+ 0, 0, -611, 0, -611, 0, 0, 0, -611, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -611, 0,
+ 0, -611, -611, 0, 0, -611, 0, -611, -611, -611,
+ -611, -611, -611, -611, -611, -611, -611, 0, 0, -588,
+ 0, 0, -611, -611, -611, -611, 0, 275, -611, -611,
+ -611, -611, -588, -588, -588, 0, -588, -588, 0, -588,
+ 0, 0, 0, 0, 0, -588, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -588, -588, 0, -588,
+ -588, -588, -588, -588, 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, -588, -588, -588, -588, -588, -588, -588, -588, -588,
+ -588, -588, -588, 0, 0, 0, 0, -588, -588, -588,
+ 0, 796, -588, 0, 0, 0, 0, 0, 0, 0,
+ -588, 0, 0, 0, -588, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -588, 0, 0, -588, -588, 0,
+ -107, -588, 0, -588, -588, -588, -588, -588, -588, -588,
+ -588, -588, -588, 0, 0, -301, 0, -588, -588, -588,
+ 0, -588, 0, 0, -588, -588, -588, -588, -301, -301,
+ -301, 0, -301, -301, 0, -301, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -301, -301, 0, -301, -301, -301, -301, -301,
+ 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, -301, -301, -301,
+ -301, -301, -301, -301, -301, -301, -301, -301, -301, 0,
+ 0, 0, 0, -301, -301, -301, 0, 797, -301, 0,
+ 0, 0, 0, 0, 0, 0, -301, 0, 0, 0,
+ -301, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -301, 0, 0, -301, -301, 0, -109, -301, 0, -301,
+ -301, -301, -301, -301, -301, -301, -301, -301, -301, 0,
+ 0, -301, 0, 0, -301, -301, 0, -101, 0, 0,
+ -301, -301, -301, -301, -301, -301, -301, 0, -301, -301,
+ 0, -301, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -301, -301,
+ 0, -301, -301, -301, -301, -301, 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, -301, -301, -301, -301, -301, -301, -301,
+ -301, -301, -301, -301, -301, 0, 0, 0, 0, -301,
+ -301, -301, 0, 797, -301, 0, 0, 0, 0, 0,
+ 0, 0, -301, 0, 0, 0, -301, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -301, 0, 0, -301,
+ -301, 0, -109, -301, 0, -301, -301, -301, -301, -301,
+ -301, -301, -301, -301, -301, 0, 0, 0, 0, 0,
+ -301, -301, 0, -301, 0, 0, -301, -301, -301, -301,
+ 295, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, -611, -611, -611, 0, 0, -611, 15, 0,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 20,
+ 21, 22, 23, 24, 25, 26, 0, 0, 27, 0,
+ 0, 0, 0, 0, 28, 0, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 0, 40, 41, 42,
+ 0, 0, 43, 0, 0, 44, 45, 0, 46, 47,
+ 48, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 49, 50, 0, 0, 0, 0, 0,
+ 51, 0, 0, 52, 53, 0, 54, 55, 0, 56,
+ 0, 0, 0, 57, 0, 58, 59, 60, 0, 61,
+ 62, 63, 0, 64, -611, 0, 0, -611, -611, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 65, 66, 67, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -611, 295, -611, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 0, 0,
+ -611, 0, -611, -611, 15, 0, 16, 17, 18, 19,
+ 0, 0, 0, 0, 0, 20, 21, 22, 23, 24,
+ 25, 26, 0, 0, 27, 0, 0, 0, 0, 0,
+ 28, 0, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 46, 47, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 50, 0, 0, 0, 0, 0, 51, 0, 0, 52,
+ 53, 0, 54, 55, 0, 56, 0, 0, 0, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ -611, 0, 0, -611, -611, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 65,
+ 66, 67, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -611, 295, -611, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 0, 0, -611, 0, 0, -611,
+ 15, -611, 16, 17, 18, 19, 0, 0, 0, 0,
+ 0, 20, 21, 22, 23, 24, 25, 26, 0, 0,
+ 27, 0, 0, 0, 0, 0, 28, 0, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 46, 47, 48, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 49, 50, 0, 0, 0,
+ 0, 0, 51, 0, 0, 52, 53, 0, 54, 55,
+ 0, 56, 0, 0, 0, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, -611, 0, 0, -611,
+ -611, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 65, 66, 67, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -611, 295, -611,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 0, 0, -611, 0, 0, -611, 15, 0, 16, 17,
+ 18, 19, 0, 0, 0, 0, 0, 20, 21, 22,
+ 23, 24, 25, 26, 0, 0, 27, 0, 0, 0,
+ 0, 0, 28, 0, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 0, 40, 41, 42, 0, 0,
+ 43, 0, 0, 44, 45, 0, 46, 47, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 49, 50, 0, 0, 0, 0, 0, 51, 0,
+ 0, 52, 53, 0, 54, 55, 0, 56, 0, 0,
+ 0, 57, 0, 58, 59, 60, 0, 61, 62, 63,
+ 0, 64, -611, 0, 0, -611, -611, 4, 0, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 0,
+ 0, 65, 66, 67, 0, 15, 0, 16, 17, 18,
+ 19, 0, 0, -611, 0, -611, 20, 21, 22, 23,
+ 24, 25, 26, 0, 0, 27, 0, 0, 0, 0,
+ 0, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 0, 40, 41, 42, 0, 0, 43,
+ 0, 0, 44, 45, 0, 46, 47, 48, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 49, 50, 0, 0, 0, 0, 0, 51, 0, 0,
+ 52, 53, 0, 54, 55, 0, 56, 0, 0, 0,
+ 57, 0, 58, 59, 60, 0, 61, 62, 63, 0,
+ 64, -611, 0, 0, -611, -611, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 65, 66, 67, 0, 0, -611, 0, 0, 0, 0,
+ 0, 0, -611, 295, -611, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 0, -611, -611, 0, 0,
+ 0, 15, 0, 16, 17, 18, 19, 0, 0, 0,
+ 0, 0, 20, 21, 22, 23, 24, 25, 26, 0,
+ 0, 27, 0, 0, 0, 0, 0, 28, 0, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 0,
+ 40, 41, 42, 0, 0, 43, 0, 0, 44, 45,
+ 0, 46, 47, 48, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 49, 50, 0, 0,
+ 0, 0, 0, 51, 0, 0, 52, 53, 0, 54,
+ 55, 0, 56, 0, 0, 0, 57, 0, 58, 59,
+ 60, 0, 61, 62, 63, 0, 64, -611, 0, 0,
+ -611, -611, 295, 0, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 0, 0, 65, 66, 67, 0,
+ 15, 0, 16, 17, 18, 19, 0, 0, -611, 0,
+ -611, 20, 21, 22, 23, 24, 25, 26, 0, 0,
+ 27, 0, 0, 0, 0, 0, 28, 0, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 46, 47, 48, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 49, 50, 0, 0, 0,
+ 0, 0, 51, 0, 0, 296, 53, 0, 54, 55,
+ 0, 56, 0, 0, 0, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, -611, 0, 0, -611,
+ -611, 295, 0, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 0, 0, 65, 66, 67, 0, 15,
+ 0, 16, 17, 18, 19, 0, -611, -611, 0, -611,
+ 20, 21, 22, 23, 24, 25, 26, 0, 0, 27,
+ 0, 0, 0, 0, 0, 28, 0, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 0, 40, 41,
+ 42, 0, 0, 43, 0, 0, 44, 45, 0, 46,
+ 47, 48, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 49, 50, 0, 0, 0, 0,
+ 0, 51, 0, 0, 52, 53, 0, 54, 55, 0,
+ 56, 0, 0, 0, 57, 0, 58, 59, 60, 0,
+ 61, 62, 63, 0, 64, -611, 0, 0, -611, -611,
+ 295, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 0, 0, 65, 66, 67, 0, 15, 0,
+ 16, 17, 18, 19, 0, -611, -611, 0, -611, 20,
+ 21, 22, 23, 24, 25, 26, 0, 0, 27, 0,
+ 0, 0, 0, 0, 28, 0, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 0, 40, 41, 42,
+ 0, 0, 43, 0, 0, 44, 45, 0, 46, 47,
+ 48, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 49, 50, 0, 0, 0, 0, 0,
+ 51, 0, 0, 52, 53, 0, 54, 55, 0, 56,
+ 0, 0, 0, 57, 0, 58, 59, 60, 0, 61,
+ 62, 63, 0, 64, -611, 0, 0, -611, -611, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 65, 66, 67, 0, 0, -611, 0,
+ 0, 0, 0, 0, 0, -611, 295, -611, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 0, 0,
+ -611, 0, 0, 0, 15, 0, 16, 17, 18, 19,
+ 0, 0, 0, 0, 0, 20, 21, 22, 23, 24,
+ 25, 26, 0, 0, 27, 0, 0, 0, 0, 0,
+ 28, 0, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 46, 47, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 50, 0, 0, 0, 0, 0, 51, 0, 0, 52,
+ 53, 0, 54, 55, 0, 56, 0, 0, 0, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ -611, 0, 0, -611, -611, 0, 0, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 0, 0, 65,
+ 66, 67, 0, 15, 0, 16, 17, 18, 19, 0,
+ 0, -611, 0, -611, 20, 21, 22, 23, 24, 25,
+ 26, 0, 0, 27, 0, 0, 0, 0, 0, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 0, 40, 41, 42, 0, 0, 43, 0, 0,
+ 44, 45, 0, 46, 47, 48, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 49, 50,
+ 0, 0, 0, 0, 0, 51, 0, 0, 52, 53,
+ 0, 54, 55, 0, 56, 0, 0, 0, 57, 0,
+ 58, 59, 60, 0, 61, 62, 63, 0, 64, 247,
+ 0, 0, 248, 249, 0, 0, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 0, 0, 65, 66,
+ 67, 0, 15, 0, 16, 17, 18, 19, 0, 0,
+ 250, 0, 251, 20, 21, 22, 23, 24, 25, 26,
+ 0, 0, 27, 0, 0, 0, 0, 0, 28, 0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 0, 40, 41, 42, 0, 0, 43, 0, 0, 44,
+ 45, 0, 46, 47, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 49, 50, 0,
+ 0, 0, 0, 0, 51, 0, 0, 52, 53, 0,
+ 54, 55, 0, 56, 0, 0, 0, 57, 0, 58,
+ 59, 60, 0, 61, 62, 63, 0, 64, 247, 0,
+ 0, 248, 249, 0, 0, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 0, 0, 0, 65, 66, 67,
+ 0, 15, 0, 16, 17, 18, 19, 0, 0, 250,
+ 0, 251, 20, 21, 22, 23, 24, 25, 26, 0,
+ 0, 27, 0, 0, 0, 0, 0, 0, 0, 0,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 0,
+ 40, 41, 42, 0, 0, 43, 0, 0, 44, 45,
+ 0, 46, 47, 48, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 49, 50, 0, 0,
+ 0, 0, 0, 211, 0, 0, 119, 53, 0, 54,
+ 55, 0, 0, 0, 0, 0, 57, 0, 58, 59,
+ 60, 0, 61, 62, 63, 0, 64, 247, 0, 0,
+ 248, 249, 0, 0, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 0, 0, 0, 65, 66, 67, 0,
+ 15, 0, 108, 109, 18, 19, 0, 0, 250, 0,
+ 251, 110, 111, 112, 23, 24, 25, 26, 0, 0,
+ 113, 0, 0, 0, 0, 0, 0, 0, 0, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 46, 47, 48, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 49, 50, 0, 0, 0,
+ 0, 0, 211, 0, 0, 119, 53, 0, 54, 55,
+ 0, 0, 0, 0, 0, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, 247, 0, 0, 248,
+ 249, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 65, 264, 67, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 250, 0, 251,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
+ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 0, 0, 0, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 0, 0, 0,
+ 0, 0, 165, 166, 167, 168, 169, 170, 171, 172,
+ 36, 37, 173, 39, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 0, 0, 182, 183, 0,
+ 0, 0, 0, 184, 185, 186, 187, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 188, 189, 190,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 0, 201, 202, 0, 0, 0, 0, 0, 0,
+ 203, 204, -581, -581, -581, -581, -581, -581, -581, -581,
+ -581, 0, 0, 0, 0, 0, 0, 0, -581, 0,
+ -581, -581, -581, -581, 0, -581, 0, 0, 0, -581,
+ -581, -581, -581, -581, -581, -581, 0, 0, -581, 0,
+ 0, 0, 0, 0, 0, 0, 0, -581, -581, -581,
+ -581, -581, -581, -581, -581, -581, 0, -581, -581, -581,
+ 0, 0, -581, 0, 0, -581, -581, 0, -581, -581,
+ -581, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -581, -581, 0, 0, 0, 0, 0,
+ -581, 0, 0, -581, -581, 0, -581, -581, 0, -581,
+ 0, -581, -581, -581, 0, -581, -581, -581, 0, -581,
+ -581, -581, 0, -581, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -581, -581, -581, 0, -581, 0, 0,
+ 0, 0, 0, -581, -582, -582, -582, -582, -582, -582,
+ -582, -582, -582, 0, 0, 0, 0, 0, 0, 0,
+ -582, 0, -582, -582, -582, -582, 0, -582, 0, 0,
+ 0, -582, -582, -582, -582, -582, -582, -582, 0, 0,
+ -582, 0, 0, 0, 0, 0, 0, 0, 0, -582,
+ -582, -582, -582, -582, -582, -582, -582, -582, 0, -582,
+ -582, -582, 0, 0, -582, 0, 0, -582, -582, 0,
+ -582, -582, -582, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -582, -582, 0, 0, 0,
+ 0, 0, -582, 0, 0, -582, -582, 0, -582, -582,
+ 0, -582, 0, -582, -582, -582, 0, -582, -582, -582,
+ 0, -582, -582, -582, 0, -582, 0, 0, 0, 0,
+ 0, 0, -584, -584, -584, -584, -584, -584, -584, -584,
+ -584, 0, 0, 0, 0, -582, -582, -582, -584, -582,
+ -584, -584, -584, -584, 0, -582, 0, 0, 0, -584,
+ -584, -584, -584, -584, -584, -584, 0, 0, -584, 0,
+ 0, 0, 0, 0, 0, 0, 0, -584, -584, -584,
+ -584, -584, -584, -584, -584, -584, 0, -584, -584, -584,
+ 0, 0, -584, 0, 0, -584, -584, 0, -584, -584,
+ -584, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -584, -584, 0, 0, 0, 0, 0,
+ -584, 828, 0, -584, -584, 0, -584, -584, 0, -584,
+ 0, -584, -584, -584, 0, -584, -584, -584, 0, -584,
+ -584, -584, 0, -584, 0, 0, 0, 0, 0, 0,
+ -107, -585, -585, -585, -585, -585, -585, -585, -585, -585,
+ 0, 0, 0, -584, -584, -584, 0, -585, 0, -585,
+ -585, -585, -585, -584, 0, 0, 0, 0, -585, -585,
+ -585, -585, -585, -585, -585, 0, 0, -585, 0, 0,
+ 0, 0, 0, 0, 0, 0, -585, -585, -585, -585,
+ -585, -585, -585, -585, -585, 0, -585, -585, -585, 0,
+ 0, -585, 0, 0, -585, -585, 0, -585, -585, -585,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -585, -585, 0, 0, 0, 0, 0, -585,
+ 829, 0, -585, -585, 0, -585, -585, 0, -585, 0,
+ -585, -585, -585, 0, -585, -585, -585, 0, -585, -585,
+ -585, 0, -585, 0, 0, 0, 0, 0, 0, -109,
+ -586, -586, -586, -586, -586, -586, -586, -586, -586, 0,
+ 0, 0, -585, -585, -585, 0, -586, 0, -586, -586,
+ -586, -586, -585, 0, 0, 0, 0, -586, -586, -586,
+ -586, -586, -586, -586, 0, 0, -586, 0, 0, 0,
+ 0, 0, 0, 0, 0, -586, -586, -586, -586, -586,
+ -586, -586, -586, -586, 0, -586, -586, -586, 0, 0,
+ -586, 0, 0, -586, -586, 0, -586, -586, -586, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -586, -586, 0, 0, 0, 0, 0, -586, 0,
+ 0, -586, -586, 0, -586, -586, 0, -586, 0, -586,
+ -586, -586, 0, -586, -586, -586, 0, -586, -586, -586,
+ 0, -586, 0, 0, 0, 0, 0, 0, -587, -587,
+ -587, -587, -587, -587, -587, -587, -587, 0, 0, 0,
+ 0, -586, -586, -586, -587, 0, -587, -587, -587, -587,
+ 0, -586, 0, 0, 0, -587, -587, -587, -587, -587,
+ -587, -587, 0, 0, -587, 0, 0, 0, 0, 0,
+ 0, 0, 0, -587, -587, -587, -587, -587, -587, -587,
+ -587, -587, 0, -587, -587, -587, 0, 0, -587, 0,
+ 0, -587, -587, 0, -587, -587, -587, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -587,
+ -587, 0, 0, 0, 0, 0, -587, 0, 0, -587,
+ -587, 0, -587, -587, 0, -587, 0, -587, -587, -587,
+ 0, -587, -587, -587, 0, -587, -587, -587, 0, -587,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -587,
+ -587, -587, 0, 0, 0, 0, 0, 0, 0, -587,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
+ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 0, 0, 0, 155, 156, 157,
+ 233, 234, 235, 236, 162, 163, 164, 0, 0, 0,
+ 0, 0, 165, 166, 167, 237, 238, 239, 240, 172,
+ 320, 321, 241, 322, 0, 0, 0, 0, 0, 0,
+ 323, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 0, 0, 182, 183, 0,
+ 0, 0, 0, 184, 185, 186, 187, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 188, 189, 190,
+ 0, 0, 0, 0, 324, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 0, 201, 202, 0, 0, 0, 0, 0, 0,
+ 203, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 0, 0, 0, 155, 156,
+ 157, 233, 234, 235, 236, 162, 163, 164, 0, 0,
+ 0, 0, 0, 165, 166, 167, 237, 238, 239, 240,
+ 172, 320, 321, 241, 322, 0, 0, 0, 0, 0,
+ 0, 323, 0, 0, 0, 0, 0, 0, 174, 175,
+ 176, 177, 178, 179, 180, 181, 0, 0, 182, 183,
+ 0, 0, 0, 0, 184, 185, 186, 187, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 188, 189,
+ 190, 0, 0, 0, 0, 485, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 0, 201, 202, 0, 0, 0, 0, 0,
+ 0, 203, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 0, 0, 0, 155,
+ 156, 157, 233, 234, 235, 236, 162, 163, 164, 0,
+ 0, 0, 0, 0, 165, 166, 167, 237, 238, 239,
+ 240, 172, 0, 0, 241, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 174,
+ 175, 176, 177, 178, 179, 180, 181, 0, 0, 182,
+ 183, 0, 0, 0, 0, 184, 185, 186, 187, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 188,
+ 189, 190, 0, 0, 0, 242, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 0, 201, 202, 0, 0, 0, 0,
+ 0, 0, 203, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 0, 0, 0,
+ 155, 156, 157, 233, 234, 235, 236, 162, 163, 164,
+ 0, 0, 0, 0, 0, 165, 166, 167, 237, 238,
+ 239, 240, 172, 0, 0, 241, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 174, 175, 176, 177, 178, 179, 180, 181, 0, 0,
+ 182, 183, 0, 0, 0, 0, 184, 185, 186, 187,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 188, 189, 190, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 191, 192, 193, 194, 195, 196,
+ 197, 198, 199, 200, 0, 201, 202, 0, 0, 0,
+ 0, 0, 0, 203, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 0, 0, 0, 0, 0, 0, 0,
+ 15, 0, 108, 109, 18, 19, 0, 0, 0, 0,
+ 0, 110, 111, 112, 23, 24, 25, 26, 0, 0,
+ 113, 0, 0, 0, 0, 0, 0, 0, 0, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 116, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 313, 0, 0, 119, 53, 0, 54, 55,
+ 0, 0, 0, 0, 0, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, 0, 0, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 0, 0, 0,
+ 0, 0, 0, 0, 15, 120, 108, 109, 18, 19,
+ 0, 0, 0, 314, 0, 110, 111, 112, 23, 24,
+ 25, 26, 0, 0, 113, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 116, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 313, 0, 0, 119,
+ 53, 0, 54, 55, 0, 0, 0, 0, 0, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ 0, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 0, 0, 0, 0, 0, 0, 15, 120,
+ 16, 17, 18, 19, 0, 0, 0, 606, 0, 20,
+ 21, 22, 23, 24, 25, 26, 0, 0, 27, 0,
+ 0, 0, 0, 0, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 0, 40, 41, 42,
+ 0, 0, 43, 0, 0, 44, 45, 0, 46, 47,
+ 48, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 49, 50, 0, 0, 0, 0, 0,
+ 51, 0, 0, 52, 53, 0, 54, 55, 0, 56,
+ 0, 0, 0, 57, 0, 58, 59, 60, 0, 61,
+ 62, 63, 0, 64, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 0, 0, 0, 65, 66, 67, 15, 0, 16, 17,
+ 18, 19, 0, 0, 0, 0, 0, 20, 21, 22,
+ 23, 24, 25, 26, 0, 0, 27, 0, 0, 0,
+ 0, 0, 28, 0, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 0, 40, 41, 42, 0, 0,
+ 43, 0, 0, 44, 45, 0, 46, 47, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 49, 50, 0, 0, 0, 0, 0, 51, 0,
+ 0, 52, 53, 0, 54, 55, 0, 56, 0, 0,
+ 0, 57, 0, 58, 59, 60, 0, 61, 62, 63,
+ 0, 64, 0, 0, 0, 0, 0, 0, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 0, 0, 0,
+ 0, 65, 66, 67, 15, 0, 16, 17, 18, 19,
+ 0, 0, 0, 0, 0, 20, 21, 22, 23, 24,
+ 25, 26, 0, 0, 113, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 32, 33, 260, 35, 36, 37,
+ 38, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 46, 47, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 50, 0, 0, 0, 0, 0, 211, 0, 0, 119,
+ 53, 0, 54, 55, 0, 261, 0, 262, 263, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ 0, 0, 0, 0, 0, 0, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 0, 0, 0, 0, 65,
+ 264, 67, 15, 0, 16, 17, 18, 19, 0, 0,
+ 0, 0, 0, 20, 21, 22, 23, 24, 25, 26,
+ 0, 0, 113, 0, 0, 0, 0, 0, 0, 0,
+ 0, 31, 32, 33, 260, 35, 36, 37, 38, 39,
+ 0, 40, 41, 42, 0, 0, 43, 0, 0, 44,
+ 45, 0, 46, 47, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 49, 507, 0,
+ 0, 0, 0, 0, 211, 0, 0, 119, 53, 0,
+ 54, 55, 0, 261, 0, 262, 263, 57, 0, 58,
+ 59, 60, 0, 61, 62, 63, 0, 64, 0, 0,
+ 0, 0, 0, 0, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 0, 0, 0, 0, 65, 264, 67,
+ 15, 0, 108, 109, 18, 19, 0, 0, 0, 0,
+ 0, 110, 111, 112, 23, 24, 25, 26, 0, 0,
+ 113, 0, 0, 0, 0, 0, 0, 0, 0, 31,
+ 32, 33, 260, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 46, 47, 48, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 49, 50, 0, 0, 0,
+ 0, 0, 211, 0, 0, 119, 53, 0, 54, 55,
+ 0, 718, 0, 262, 263, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, 0, 0, 0, 0,
+ 0, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 0, 0, 0, 0, 65, 264, 67, 15, 0,
+ 108, 109, 18, 19, 0, 0, 0, 0, 0, 110,
+ 111, 112, 23, 24, 25, 26, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0, 0, 31, 32, 33,
+ 260, 35, 36, 37, 38, 39, 0, 40, 41, 42,
+ 0, 0, 43, 0, 0, 44, 45, 0, 46, 47,
+ 48, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 49, 846, 0, 0, 0, 0, 0,
+ 211, 0, 0, 119, 53, 0, 54, 55, 0, 718,
+ 0, 262, 263, 57, 0, 58, 59, 60, 0, 61,
+ 62, 63, 0, 64, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 0,
+ 0, 0, 0, 65, 264, 67, 15, 0, 108, 109,
+ 18, 19, 0, 0, 0, 0, 0, 110, 111, 112,
+ 23, 24, 25, 26, 0, 0, 113, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31, 32, 33, 260, 35,
+ 36, 37, 38, 39, 0, 40, 41, 42, 0, 0,
+ 43, 0, 0, 44, 45, 0, 46, 47, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 49, 50, 0, 0, 0, 0, 0, 211, 0,
+ 0, 119, 53, 0, 54, 55, 0, 261, 0, 262,
+ 0, 57, 0, 58, 59, 60, 0, 61, 62, 63,
+ 0, 64, 0, 0, 0, 0, 0, 0, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 0, 0, 0,
+ 0, 65, 264, 67, 15, 0, 108, 109, 18, 19,
+ 0, 0, 0, 0, 0, 110, 111, 112, 23, 24,
+ 25, 26, 0, 0, 113, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 32, 33, 260, 35, 36, 37,
+ 38, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 46, 47, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 50, 0, 0, 0, 0, 0, 211, 0, 0, 119,
+ 53, 0, 54, 55, 0, 0, 0, 262, 263, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ 0, 0, 0, 0, 0, 0, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 0, 0, 0, 0, 65,
+ 264, 67, 15, 0, 108, 109, 18, 19, 0, 0,
+ 0, 0, 0, 110, 111, 112, 23, 24, 25, 26,
+ 0, 0, 113, 0, 0, 0, 0, 0, 0, 0,
+ 0, 31, 32, 33, 260, 35, 36, 37, 38, 39,
+ 0, 40, 41, 42, 0, 0, 43, 0, 0, 44,
+ 45, 0, 46, 47, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 49, 50, 0,
+ 0, 0, 0, 0, 211, 0, 0, 119, 53, 0,
+ 54, 55, 0, 718, 0, 262, 0, 57, 0, 58,
+ 59, 60, 0, 61, 62, 63, 0, 64, 0, 0,
+ 0, 0, 0, 0, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 0, 0, 0, 0, 65, 264, 67,
+ 15, 0, 108, 109, 18, 19, 0, 0, 0, 0,
+ 0, 110, 111, 112, 23, 24, 25, 26, 0, 0,
+ 113, 0, 0, 0, 0, 0, 0, 0, 0, 31,
+ 32, 33, 260, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 46, 47, 48, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 49, 50, 0, 0, 0,
+ 0, 0, 211, 0, 0, 119, 53, 0, 54, 55,
+ 0, 0, 0, 262, 0, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, 0, 0, 0, 0,
+ 0, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 0, 0, 0, 0, 65, 264, 67, 15, 0,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 20,
+ 21, 22, 23, 24, 25, 26, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0, 0, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 0, 40, 41, 42,
+ 0, 0, 43, 0, 0, 44, 45, 0, 46, 47,
+ 48, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 49, 50, 0, 0, 0, 0, 0,
+ 211, 0, 0, 119, 53, 0, 54, 55, 0, 600,
+ 0, 0, 0, 57, 0, 58, 59, 60, 0, 61,
+ 62, 63, 0, 64, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 0,
+ 0, 0, 0, 65, 264, 67, 15, 0, 108, 109,
+ 18, 19, 0, 0, 0, 0, 0, 110, 111, 112,
+ 23, 24, 25, 26, 0, 0, 113, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 0, 40, 41, 42, 0, 0,
+ 43, 0, 0, 44, 45, 0, 46, 47, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 49, 50, 0, 0, 0, 0, 0, 211, 0,
+ 0, 119, 53, 0, 54, 55, 0, 261, 0, 0,
+ 0, 57, 0, 58, 59, 60, 0, 61, 62, 63,
+ 0, 64, 0, 0, 0, 0, 0, 0, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 0, 0, 0,
+ 0, 65, 264, 67, 15, 0, 108, 109, 18, 19,
+ 0, 0, 0, 0, 0, 110, 111, 112, 23, 24,
+ 25, 26, 0, 0, 113, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 46, 47, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 50, 0, 0, 0, 0, 0, 211, 0, 0, 119,
+ 53, 0, 54, 55, 0, 600, 0, 0, 0, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ 0, 0, 0, 0, 0, 0, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 0, 0, 0, 0, 65,
+ 264, 67, 15, 0, 108, 109, 18, 19, 0, 0,
+ 0, 0, 0, 110, 111, 112, 23, 24, 25, 26,
+ 0, 0, 113, 0, 0, 0, 0, 0, 0, 0,
+ 0, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 0, 40, 41, 42, 0, 0, 43, 0, 0, 44,
+ 45, 0, 46, 47, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 49, 50, 0,
+ 0, 0, 0, 0, 211, 0, 0, 119, 53, 0,
+ 54, 55, 0, 891, 0, 0, 0, 57, 0, 58,
+ 59, 60, 0, 61, 62, 63, 0, 64, 0, 0,
+ 0, 0, 0, 0, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 0, 0, 0, 0, 65, 264, 67,
+ 15, 0, 108, 109, 18, 19, 0, 0, 0, 0,
+ 0, 110, 111, 112, 23, 24, 25, 26, 0, 0,
+ 113, 0, 0, 0, 0, 0, 0, 0, 0, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 46, 47, 48, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 49, 50, 0, 0, 0,
+ 0, 0, 211, 0, 0, 119, 53, 0, 54, 55,
+ 0, 718, 0, 0, 0, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, 0, 0, 0, 0,
+ 0, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 0, 0, 0, 0, 65, 264, 67, 15, 0,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 20,
+ 21, 22, 23, 24, 25, 26, 0, 0, 27, 0,
+ 0, 0, 0, 0, 0, 0, 0, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 0, 40, 41, 42,
+ 0, 0, 43, 0, 0, 44, 45, 0, 46, 47,
+ 48, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 49, 50, 0, 0, 0, 0, 0,
+ 211, 0, 0, 119, 53, 0, 54, 55, 0, 0,
+ 0, 0, 0, 57, 0, 58, 59, 60, 0, 61,
+ 62, 63, 0, 64, 0, 0, 0, 0, 0, 0,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 0,
+ 0, 0, 0, 65, 66, 67, 15, 0, 108, 109,
+ 18, 19, 0, 0, 0, 0, 0, 110, 111, 112,
+ 23, 24, 25, 26, 0, 0, 113, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 0, 40, 41, 42, 0, 0,
+ 43, 0, 0, 44, 45, 0, 46, 47, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 49, 50, 0, 0, 0, 0, 0, 211, 0,
+ 0, 119, 53, 0, 54, 55, 0, 0, 0, 0,
+ 0, 57, 0, 58, 59, 60, 0, 61, 62, 63,
+ 0, 64, 0, 0, 0, 0, 0, 0, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 0, 0, 0,
+ 0, 65, 264, 67, 15, 0, 16, 17, 18, 19,
+ 0, 0, 0, 0, 0, 20, 21, 22, 23, 24,
+ 25, 26, 0, 0, 113, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 46, 47, 48, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 50, 0, 0, 0, 0, 0, 211, 0, 0, 119,
+ 53, 0, 54, 55, 0, 0, 0, 0, 0, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ 0, 0, 0, 0, 0, 0, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 0, 0, 0, 0, 65,
+ 264, 67, 15, 0, 108, 109, 18, 19, 0, 0,
+ 0, 0, 0, 110, 111, 112, 23, 24, 25, 26,
+ 0, 0, 113, 0, 0, 0, 0, 0, 0, 0,
+ 0, 31, 32, 33, 114, 35, 36, 37, 115, 39,
+ 0, 40, 41, 42, 0, 0, 43, 0, 0, 44,
+ 45, 0, 116, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 117, 0, 0, 118, 0, 0, 119, 53, 0,
+ 54, 55, 0, 0, 0, 0, 0, 57, 0, 58,
+ 59, 60, 0, 61, 62, 63, 0, 64, 0, 0,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 0,
+ 0, 0, 0, 0, 0, 0, 15, 120, 108, 109,
+ 18, 19, 0, 0, 0, 0, 0, 110, 111, 112,
+ 23, 24, 25, 26, 0, 0, 113, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 0, 40, 41, 42, 0, 0,
+ 43, 0, 0, 44, 45, 0, 225, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 226, 0,
+ 0, 52, 53, 0, 54, 55, 0, 56, 0, 0,
+ 0, 57, 0, 58, 59, 60, 0, 61, 62, 63,
+ 0, 64, 0, 0, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 0, 0, 0, 0, 0, 0, 0,
+ 15, 120, 108, 109, 18, 19, 0, 0, 0, 0,
+ 0, 110, 111, 112, 23, 24, 25, 26, 0, 0,
+ 113, 0, 0, 0, 0, 0, 0, 0, 0, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 0, 40,
+ 41, 42, 0, 0, 43, 0, 0, 44, 45, 0,
+ 116, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 313, 0, 0, 398, 53, 0, 54, 55,
+ 0, 399, 0, 0, 0, 57, 0, 58, 59, 60,
+ 0, 61, 62, 63, 0, 64, 0, 0, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 0, 0, 0,
+ 0, 0, 0, 0, 15, 120, 108, 109, 18, 19,
+ 0, 0, 0, 0, 0, 110, 111, 112, 23, 24,
+ 25, 26, 0, 0, 113, 0, 0, 0, 0, 0,
+ 0, 0, 0, 31, 32, 33, 114, 35, 36, 37,
+ 115, 39, 0, 40, 41, 42, 0, 0, 43, 0,
+ 0, 44, 45, 0, 116, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 118, 0, 0, 119,
+ 53, 0, 54, 55, 0, 0, 0, 0, 0, 57,
+ 0, 58, 59, 60, 0, 61, 62, 63, 0, 64,
+ 0, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 0, 0, 0, 0, 0, 0, 0, 15, 120,
+ 108, 109, 18, 19, 0, 0, 0, 0, 0, 110,
+ 111, 112, 23, 24, 25, 26, 0, 0, 113, 0,
+ 0, 0, 0, 0, 0, 0, 0, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 0, 40, 41, 42,
+ 0, 0, 43, 0, 0, 44, 45, 0, 116, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 313, 0, 0, 398, 53, 0, 54, 55, 0, 0,
+ 0, 0, 0, 57, 0, 58, 59, 60, 0, 61,
+ 62, 63, 0, 64, 0, 0, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 0, 0, 0, 0, 0,
+ 0, 0, 15, 120, 108, 109, 18, 19, 0, 0,
+ 0, 0, 0, 110, 111, 112, 23, 24, 25, 26,
+ 0, 0, 113, 0, 0, 0, 0, 0, 0, 0,
+ 0, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 0, 40, 41, 42, 0, 0, 43, 0, 0, 44,
+ 45, 0, 116, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 958, 0, 0, 119, 53, 0,
+ 54, 55, 0, 0, 0, 0, 0, 57, 0, 58,
+ 59, 60, 0, 61, 62, 63, 0, 64, 0, 0,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 0,
+ 0, 0, 0, 0, 0, 0, 15, 120, 108, 109,
+ 18, 19, 0, 0, 0, 0, 0, 110, 111, 112,
+ 23, 24, 25, 26, 0, 0, 113, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 0, 40, 41, 42, 0, 0,
+ 43, 0, 0, 44, 45, 0, 225, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 981, 0,
+ 0, 119, 53, 0, 54, 55, 0, 0, 654, 655,
+ 0, 57, 656, 58, 59, 60, 0, 61, 62, 63,
+ 0, 64, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 0, 0, 182, 183, 0,
+ 0, 120, 0, 184, 185, 186, 187, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 188, 189, 190,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 0, 201, 202, 675, 646, 0, 0, 676, 0,
+ 203, 275, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 174, 175, 176, 177, 178, 179, 180,
+ 181, 0, 0, 182, 183, 0, 0, 0, 0, 184,
+ 185, 186, 187, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 188, 189, 190, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 191, 192, 193,
+ 194, 195, 196, 197, 198, 199, 200, 0, 201, 202,
+ 660, 655, 0, 0, 661, 0, 203, 275, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 174,
+ 175, 176, 177, 178, 179, 180, 181, 0, 0, 182,
+ 183, 0, 0, 0, 0, 184, 185, 186, 187, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 188,
+ 189, 190, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 0, 201, 202, 692, 646, 0, 0,
+ 693, 0, 203, 275, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 174, 175, 176, 177, 178,
+ 179, 180, 181, 0, 0, 182, 183, 0, 0, 0,
+ 0, 184, 185, 186, 187, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 188, 189, 190, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 0,
+ 201, 202, 695, 655, 0, 0, 696, 0, 203, 275,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 174, 175, 176, 177, 178, 179, 180, 181, 0,
+ 0, 182, 183, 0, 0, 0, 0, 184, 185, 186,
+ 187, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 188, 189, 190, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 0, 201, 202, 702, 646,
+ 0, 0, 703, 0, 203, 275, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 0, 0, 182, 183, 0,
+ 0, 0, 0, 184, 185, 186, 187, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 188, 189, 190,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 0, 201, 202, 705, 655, 0, 0, 706, 0,
+ 203, 275, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 174, 175, 176, 177, 178, 179, 180,
+ 181, 0, 0, 182, 183, 0, 0, 0, 0, 184,
+ 185, 186, 187, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 188, 189, 190, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 191, 192, 193,
+ 194, 195, 196, 197, 198, 199, 200, 0, 201, 202,
+ 741, 646, 0, 0, 742, 0, 203, 275, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 174,
+ 175, 176, 177, 178, 179, 180, 181, 0, 0, 182,
+ 183, 0, 0, 0, 0, 184, 185, 186, 187, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 188,
+ 189, 190, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 0, 201, 202, 744, 655, 0, 0,
+ 745, 0, 203, 275, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 174, 175, 176, 177, 178,
+ 179, 180, 181, 0, 0, 182, 183, 0, 0, 0,
+ 0, 184, 185, 186, 187, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 188, 189, 190, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 0,
+ 201, 202, 896, 646, 0, 0, 897, 0, 203, 275,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 174, 175, 176, 177, 178, 179, 180, 181, 0,
+ 0, 182, 183, 0, 0, 0, 0, 184, 185, 186,
+ 187, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 188, 189, 190, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 0, 201, 202, 899, 655,
+ 0, 0, 900, 0, 203, 275, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 174, 175, 176,
+ 177, 178, 179, 180, 181, 0, 0, 182, 183, 0,
+ 0, 0, 0, 184, 185, 186, 187, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 188, 189, 190,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 0, 201, 202, 1040, 646, 0, 0, 1041, 0,
+ 203, 275, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 174, 175, 176, 177, 178, 179, 180,
+ 181, 0, 0, 182, 183, 0, 0, 0, 0, 184,
+ 185, 186, 187, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 188, 189, 190, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 191, 192, 193,
+ 194, 195, 196, 197, 198, 199, 200, 0, 201, 202,
+ 1052, 646, 0, 0, 1053, 0, 203, 275, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 174,
+ 175, 176, 177, 178, 179, 180, 181, 0, 0, 182,
+ 183, 0, 0, 0, 0, 184, 185, 186, 187, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 188,
+ 189, 190, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 0, 201, 202, 1055, 655, 0, 0,
+ 1056, 0, 203, 275, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 174, 175, 176, 177, 178,
+ 179, 180, 181, 0, 0, 182, 183, 0, 0, 0,
+ 0, 184, 185, 186, 187, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 188, 189, 190, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 0,
+ 201, 202, 660, 655, 0, 0, 661, 0, 203, 275,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 174, 175, 176, 177, 178, 179, 180, 181, 0,
+ 0, 182, 183, 0, 0, 0, 0, 184, 185, 186,
+ 187, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 188, 189, 190, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 791, 0, 0,
+ 0, 0, 0, 0, 0, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 0, 201, 202, 0, 0,
+ 0, 0, 0, 0, 203, 402, 403, 404, 405, 406,
+ 407, 408, 409, 410, 411, 412, 413, 0, 0, 0,
+ 0, 414, 415, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 417, 0, 0, 0, 0, 864,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 418, 0, 419, 420, 421,
+ 422, 423, 424, 425, 426, 427, 428, 402, 403, 404,
+ 405, 406, 407, 408, 409, 410, 411, 412, 413, 0,
+ 0, 0, 0, 414, 415, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 417, 0, 0, 0,
+ 0, 876, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 418, 0, 419,
+ 420, 421, 422, 423, 424, 425, 426, 427, 428, 402,
+ 403, 404, 405, 406, 407, 408, 409, 410, 411, 412,
+ 413, 0, 0, 0, 0, 414, 415, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 417, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 418,
+ 0, 419, 420, 421, 422, 423, 424, 425, 426, 427,
+ 428, 402, 403, 404, 405, 406, 407, 408, 409, 410,
+ 411, 412, 413, 0, 0, 0, 0, 414, 415, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 417, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 418, 0, 419, 420, 421, 422, 423, 424, 425,
+ 426, 427, 428, 402, 403, 404, 405, 406, 407, 408,
+ 409, 410, 411, 412, 413, 0, 0, 251, 0, 414,
+ 415, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 417, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 418, 0, 419, 420, 421, 422, 423,
+ 424, 425, 426, 427, 428, 0, 0, 0, 0, 0,
+ 0, 0, 0, -277, 402, 403, 404, 405, 406, 407,
+ 408, 409, 410, 411, 412, 413, 0, 0, 0, 0,
+ 414, 415, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 417, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 418, 0, 419, 420, 421, 422,
+ 423, 424, 425, 426, 427, 428, 0, 0, 0, 0,
+ 0, 0, 0, 0, -278, 402, 403, 404, 405, 406,
+ 407, 408, 409, 410, 411, 412, 413, 0, 0, 0,
+ 0, 414, 415, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 417, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 418, 0, 419, 420, 421,
+ 422, 423, 424, 425, 426, 427, 428, 0, 0, 0,
+ 0, 0, 0, 0, 0, -279, 402, 403, 404, 405,
+ 406, 407, 408, 409, 410, 411, 412, 413, 0, 0,
+ 0, 0, 414, 415, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 417, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 418, 0, 419, 420,
+ 421, 422, 423, 424, 425, 426, 427, 428, 0, 0,
+ 0, 0, 0, 0, 0, 0, -280, 402, 403, 404,
+ 405, 406, 407, 408, 409, 410, 411, 412, 413, 0,
+ 0, 0, 0, 414, 415, 0, 0, 0, 416, 0,
+ 0, 0, 0, 0, 0, 0, 417, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 418, 0, 419,
+ 420, 421, 422, 423, 424, 425, 426, 427, 428, 402,
+ 403, 404, 405, 406, 407, 408, 409, 410, 411, 412,
+ 413, 0, 0, 0, 0, 414, 415, 0, 0, 0,
+ 499, 0, 0, 0, 0, 0, 0, 0, 417, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 418,
+ 0, 419, 420, 421, 422, 423, 424, 425, 426, 427,
+ 428, 402, 403, 404, 405, 406, 407, 408, 409, 410,
+ 411, 412, 413, 0, 0, 0, 0, 414, 415, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 417, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 418, 0, 419, 420, 421, 422, 423, 424, 425,
+ 426, 427, 428, 402, 403, 404, 405, 406, 407, 408,
+ 409, 410, 411, -612, -612, 0, 0, 0, 0, 414,
+ 415, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 417, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 419, 420, 421, 422, 423,
+ 424, 425, 426, 427, 428
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 2, 27, 27, 16, 17, 87, 88, 20, 89, 222,
+ 2, 10, 4, 5, 6, 271, 15, 9, 10, 21,
+ 377, 13, 82, 15, 16, 17, 7, 14, 20, 314,
+ 488, 28, 2, 14, 4, 588, 66, 5, 6, 22,
+ 118, 28, 16, 17, 479, 13, 20, 28, 4, 591,
+ 7, 331, 54, 55, 431, 16, 17, 15, 307, 20,
+ 52, 754, 311, 401, 56, 780, 60, 61, 62, 63,
+ 588, 56, 74, 75, 66, 52, 296, 58, 69, 456,
+ 54, 55, 16, 17, 686, 16, 20, 502, 56, 75,
+ 82, 506, 25, 54, 544, 697, 473, 663, 664, 75,
+ 494, 58, 21, 22, 941, 482, 29, 506, 26, 111,
+ 26, 69, 25, 319, 82, 103, 25, 966, 57, 25,
+ 54, 55, 105, 25, 0, 117, 92, 119, 25, 25,
+ 16, 17, 25, 57, 20, 588, 218, 536, 591, 90,
+ 128, 594, 79, 799, 58, 59, 122, 229, 90, 805,
+ 90, 372, 37, 38, 52, 121, 606, 91, 269, 121,
+ 271, 55, 90, 369, 659, 105, 18, 662, 20, 546,
+ 9, 10, 393, 90, 332, 138, 15, 335, 398, 337,
+ 142, 339, 121, 341, 115, 680, 105, 118, 119, 126,
+ 113, 121, 111, 112, 1043, 306, 147, 1034, 51, 123,
+ 53, 54, 55, 56, 28, 147, 759, 147, 290, 142,
+ 129, 213, 214, 142, 494, 146, 69, 148, 210, 147,
+ 138, 119, 138, 90, 223, 224, 144, 213, 214, 142,
+ 147, 223, 224, 142, 316, 144, 142, 458, 214, 121,
+ 142, 759, 144, 92, 810, 142, 142, 92, 963, 142,
+ 92, 966, 90, 946, 314, 511, 429, 92, 142, 544,
+ 433, 242, 275, 436, 148, 16, 279, 269, 125, 271,
+ 441, 442, 121, 275, 554, 144, 121, 90, 117, 121,
+ 147, 144, 90, 275, 457, 242, 90, 279, 55, 142,
+ 121, 283, 284, 215, 252, 92, 288, 105, 90, 472,
+ 222, 474, 55, 295, 296, 279, 759, 298, 861, 147,
+ 483, 303, 727, 728, 275, 306, 307, 870, 279, 296,
+ 311, 606, 314, 717, 866, 295, 58, 59, 1043, 728,
+ 324, 90, 140, 303, 147, 937, 144, 259, 788, 147,
+ 298, 275, 548, 147, 144, 279, 314, 685, 148, 522,
+ 349, 350, 351, 352, 25, 147, 348, 349, 350, 351,
+ 352, 353, 354, 355, 115, 51, 396, 118, 119, 55,
+ 142, 401, 20, 375, 547, 377, 92, 455, 348, 142,
+ 372, 57, 401, 353, 223, 224, 801, 101, 147, 138,
+ 288, 142, 348, 279, 552, 146, 51, 148, 296, 510,
+ 511, 393, 837, 92, 396, 121, 398, 399, 765, 401,
+ 963, 441, 442, 866, 399, 868, 793, 870, 431, 872,
+ 145, 398, 441, 442, 121, 60, 348, 139, 63, 431,
+ 92, 399, 121, 141, 714, 396, 55, 717, 142, 431,
+ 401, 92, 101, 456, 283, 284, 101, 102, 103, 441,
+ 442, 101, 433, 674, 456, 436, 101, 379, 121, 121,
+ 473, 121, 464, 142, 456, 721, 458, 459, 51, 482,
+ 121, 473, 107, 128, 500, 500, 457, 469, 92, 937,
+ 482, 473, 542, 1036, 544, 477, 131, 132, 133, 738,
+ 482, 142, 603, 474, 1047, 487, 142, 431, 588, 712,
+ 398, 591, 483, 788, 17, 18, 92, 121, 510, 511,
+ 349, 350, 351, 352, 433, 354, 355, 519, 92, 878,
+ 879, 518, 456, 51, 939, 882, 883, 519, 502, 142,
+ 491, 518, 26, 546, 616, 121, 528, 518, 457, 473,
+ 939, 522, 653, 142, 546, 142, 606, 121, 482, 519,
+ 542, 509, 544, 1006, 546, 474, 478, 479, 528, 533,
+ 781, 553, 51, 843, 483, 578, 547, 90, 502, 16,
+ 121, 469, 506, 90, 542, 386, 544, 388, 92, 477,
+ 121, 122, 105, 142, 597, 51, 578, 808, 105, 487,
+ 99, 847, 15, 92, 13, 121, 90, 599, 16, 533,
+ 121, 712, 536, 522, 63, 597, 528, 121, 634, 634,
+ 721, 105, 546, 535, 606, 15, 145, 140, 145, 792,
+ 459, 794, 121, 140, 147, 796, 142, 2, 547, 4,
+ 147, 802, 803, 854, 9, 10, 92, 139, 606, 142,
+ 15, 16, 17, 142, 138, 20, 140, 15, 44, 369,
+ 144, 653, 1029, 147, 142, 553, 92, 659, 1015, 15,
+ 662, 663, 664, 121, 141, 121, 27, 650, 115, 759,
+ 141, 118, 119, 15, 673, 18, 659, 52, 680, 662,
+ 92, 673, 674, 685, 686, 121, 142, 141, 690, 90,
+ 780, 66, 581, 581, 139, 697, 585, 141, 919, 146,
+ 15, 148, 683, 141, 105, 92, 142, 139, 707, 121,
+ 923, 708, 139, 148, 142, 707, 929, 44, 90, 721,
+ 746, 708, 57, 44, 685, 806, 683, 708, 788, 142,
+ 142, 650, 142, 105, 121, 142, 847, 908, 142, 140,
+ 659, 15, 117, 662, 119, 93, 147, 738, 115, 90,
+ 672, 118, 119, 727, 115, 142, 14, 118, 119, 678,
+ 790, 680, 15, 765, 105, 15, 796, 797, 140, 145,
+ 15, 790, 802, 803, 15, 147, 866, 796, 145, 146,
+ 793, 148, 26, 802, 803, 146, 146, 148, 142, 781,
+ 712, 793, 142, 727, 728, 142, 788, 789, 790, 140,
+ 15, 793, 142, 142, 796, 797, 147, 141, 810, 139,
+ 802, 803, 15, 794, 15, 139, 808, 809, 820, 789,
+ 788, 823, 90, 142, 126, 300, 825, 801, 61, 304,
+ 822, 64, 65, 825, 673, 210, 115, 105, 126, 118,
+ 119, 55, 834, 835, 139, 847, 90, 15, 223, 224,
+ 842, 55, 878, 879, 574, 142, 142, 1030, 142, 793,
+ 142, 105, 854, 855, 142, 142, 115, 801, 707, 118,
+ 119, 591, 140, 963, 594, 794, 966, 144, 908, 147,
+ 882, 883, 16, 116, 117, 15, 141, 144, 880, 908,
+ 812, 780, 780, 885, 138, 142, 140, 146, 519, 148,
+ 275, 983, 13, 147, 279, 441, 442, 6, 283, 284,
+ 1032, 809, 880, 288, 780, 837, 908, 885, 1034, 1031,
+ 295, 296, 37, 38, 822, 254, 918, 919, 303, 115,
+ 922, 7, 118, 119, 926, 937, 834, 835, 581, 475,
+ 476, 90, 807, 780, 842, 51, 960, 53, 54, 55,
+ 56, 963, 90, 1043, 922, -1, 105, 855, 769, 770,
+ 771, 270, 773, 69, 775, -1, -1, 105, -1, -1,
+ 996, 996, -1, 348, 349, 350, 351, 352, 353, 354,
+ 355, 115, -1, -1, 118, 119, 825, 523, 887, 888,
+ -1, 140, -1, -1, 986, 90, 988, 372, 147, 991,
+ 115, 923, 140, 118, 119, 939, -1, 929, -1, 147,
+ 105, -1, 146, 1015, 148, -1, 1029, -1, 393, 90,
+ 918, 396, -1, 398, -1, -1, 401, 1029, 926, 1031,
+ 1032, 146, 507, 148, 105, -1, 142, 1029, -1, 514,
+ -1, -1, 90, -1, -1, 140, 90, 90, -1, 1030,
+ 525, 62, 147, 64, 65, -1, 431, 105, 90, -1,
+ 780, 105, 105, -1, 963, -1, 441, 442, -1, 140,
+ 26, 960, 960, 105, 963, 963, 147, 966, 966, 968,
+ 968, 456, 61, 458, 459, 64, 65, -1, 986, -1,
+ 988, -1, 140, 991, 469, 1029, 140, 140, 473, 147,
+ 575, 576, 477, 147, 147, 116, 117, 482, 140, -1,
+ -1, 1030, 487, -1, 62, 147, 64, 65, -1, -1,
+ 1019, 1020, 1021, 960, 1023, 1024, 963, 1016, 1016, 966,
+ 605, 968, -1, -1, 90, 26, -1, 116, 117, 950,
+ 951, 952, 953, -1, 519, -1, 866, -1, 868, 105,
+ -1, -1, 872, 528, 1043, 1043, 1045, 1045, 1047, 1047,
+ 1049, 1049, -1, -1, 1063, 1064, 1065, 1066, 116, 117,
+ -1, 546, 115, -1, 1073, 118, 119, -1, 553, 1016,
+ 1069, 1069, 138, -1, 140, -1, -1, -1, 144, -1,
+ -1, 147, -1, 26, 26, -1, -1, -1, -1, 90,
+ -1, -1, -1, 578, -1, 148, 1043, 682, 1045, -1,
+ 1047, -1, 1049, -1, 105, -1, 51, -1, 53, 54,
+ 55, 56, 597, -1, 944, 945, -1, -1, 1039, -1,
+ 9, 10, 1069, -1, 69, -1, 15, 16, 17, -1,
+ -1, 20, -1, -1, -1, -1, 966, 138, 968, 140,
+ 63, 64, 65, 144, -1, -1, 147, 90, 90, 94,
+ 796, 797, 737, -1, -1, 100, 802, 803, 47, 48,
+ 49, 50, 105, 105, -1, 54, 55, 63, 64, 65,
+ 755, -1, -1, 1003, -1, -1, 1006, 66, 67, -1,
+ -1, -1, 828, 829, -1, 831, 832, -1, 673, 674,
+ 63, 64, 65, 116, 117, 138, 138, 140, 140, -1,
+ -1, 144, 144, -1, 147, 147, -1, 51, 1038, 53,
+ 54, 55, 56, 1043, -1, 1045, -1, -1, -1, 1049,
+ 116, 117, 707, 101, -1, 69, -1, -1, 117, 51,
+ -1, 53, 54, 55, 56, 63, 64, 65, -1, 1069,
+ 63, 64, 65, 116, 117, -1, -1, 69, -1, -1,
+ 94, 129, 130, 131, 132, 133, 100, 101, 102, 103,
+ -1, 846, 908, -1, -1, 2, -1, 4, 5, 6,
+ -1, -1, 94, 63, 64, 65, 13, 862, 100, 101,
+ 102, 103, -1, -1, 128, 931, -1, 131, 116, 117,
+ 63, 64, 65, 116, 117, -1, 781, -1, -1, 121,
+ 144, 63, 64, 65, 789, 790, 128, -1, 793, 131,
+ -1, 796, 797, -1, -1, 52, -1, 802, 803, 56,
+ -1, -1, 144, 808, 809, -1, 116, 117, 51, -1,
+ 53, 54, 55, 56, 223, 224, -1, 822, -1, -1,
+ 825, -1, -1, 116, 117, 82, 69, -1, -1, 834,
+ 835, -1, -1, -1, 116, 117, 115, 842, -1, 118,
+ 119, -1, -1, 51, -1, 53, 54, 55, 56, 854,
+ 855, 94, 261, 262, 263, 264, -1, 100, 101, 102,
+ 103, 69, 119, 142, -1, -1, 275, 146, -1, 148,
+ 279, -1, -1, -1, 283, 284, -1, -1, -1, 51,
+ -1, 53, 54, 55, 56, 128, 94, -1, 131, -1,
+ -1, -1, 100, 101, 102, 103, -1, 69, -1, -1,
+ -1, 144, -1, 908, -1, -1, 51, -1, 53, 54,
+ 55, 56, -1, 918, 919, 40, 41, 42, 43, 44,
+ 128, 926, 94, 131, 69, 88, 89, -1, 100, 101,
+ 102, 103, -1, -1, 142, 88, 89, -1, 101, -1,
+ 349, 350, 351, 352, -1, 354, 355, -1, 101, 94,
+ -1, -1, -1, 210, -1, 100, 128, -1, -1, 131,
+ -1, -1, -1, -1, 373, 128, 129, 130, 131, 132,
+ 133, -1, -1, -1, -1, 384, 129, 130, 131, 132,
+ 133, 986, -1, 988, -1, -1, 991, 396, -1, -1,
+ -1, -1, 401, 402, 403, 404, 405, 406, 407, 408,
+ 409, 410, 411, 412, 413, 414, 415, -1, 417, 418,
+ 419, 420, 421, 422, 423, 424, 425, 426, 427, 428,
+ -1, -1, 431, -1, 1029, 51, -1, 53, 54, 55,
+ 56, 288, 441, 442, -1, -1, -1, -1, 295, 296,
+ -1, -1, -1, 69, -1, -1, 303, 456, -1, -1,
+ 459, 51, -1, 53, 54, 55, 56, 314, -1, 85,
+ -1, -1, 471, -1, 473, -1, 475, 476, 94, 69,
+ -1, -1, -1, 482, 100, 101, 102, 103, -1, -1,
+ -1, -1, 491, -1, -1, -1, 495, -1, -1, -1,
+ 499, 348, -1, 502, 94, 504, 353, 506, 507, -1,
+ -1, -1, 128, -1, 51, 131, 53, 54, 55, 56,
+ -1, -1, -1, -1, 523, 372, -1, 2, -1, 4,
+ 5, 6, 69, -1, 533, -1, -1, 536, 13, 51,
+ -1, 53, 54, 55, 56, -1, 393, 546, -1, -1,
+ -1, 398, 399, -1, 401, -1, -1, 69, -1, -1,
+ -1, -1, -1, 562, 563, -1, -1, -1, -1, -1,
+ -1, -1, -1, 85, -1, -1, -1, 52, -1, 578,
+ -1, 56, 94, -1, -1, -1, -1, -1, 100, 101,
+ 102, 103, -1, -1, 441, 442, -1, -1, 597, -1,
+ 51, 600, 53, 54, 55, 56, -1, 82, -1, -1,
+ -1, 458, -1, -1, -1, -1, 128, -1, 69, 131,
+ 88, 89, 469, -1, -1, -1, -1, -1, -1, -1,
+ 477, -1, -1, 101, -1, -1, -1, -1, -1, -1,
+ 487, -1, -1, 94, 119, -1, -1, -1, -1, -1,
+ 101, 102, 103, -1, -1, -1, -1, -1, 126, 127,
+ 128, 129, 130, 131, 132, 133, -1, -1, -1, -1,
+ -1, -1, 519, -1, 673, -1, -1, 128, -1, -1,
+ -1, 528, -1, -1, -1, -1, 685, -1, -1, 688,
+ 689, -1, -1, -1, -1, 542, -1, 544, -1, -1,
+ -1, -1, -1, -1, -1, -1, 553, -1, 707, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 718,
+ -1, -1, -1, -1, -1, -1, -1, -1, 727, 728,
+ -1, -1, -1, -1, -1, 210, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 606,
+ -1, -1, -1, -1, -1, 2, -1, 4, 5, 6,
+ -1, -1, -1, -1, -1, -1, 13, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 785, -1, -1, -1,
+ -1, 790, 791, -1, 793, -1, -1, 796, 797, -1,
+ -1, -1, 801, 802, 803, -1, -1, -1, -1, -1,
+ -1, -1, -1, 288, -1, 52, -1, -1, -1, 56,
+ 295, 296, -1, -1, -1, -1, 825, 674, 303, 828,
+ 829, -1, 831, 832, -1, -1, -1, -1, -1, 314,
+ -1, -1, 841, -1, -1, 82, -1, 846, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 863, 864, -1, -1, -1, -1,
+ -1, -1, -1, 348, -1, -1, 875, 876, 353, -1,
+ -1, -1, 119, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 891, -1, -1, -1, -1, 372, -1, -1,
+ -1, -1, 901, 902, -1, -1, -1, -1, -1, 908,
+ -1, -1, -1, -1, -1, -1, -1, -1, 393, -1,
+ -1, -1, -1, 398, 399, -1, 401, -1, -1, -1,
+ -1, -1, 931, -1, 781, -1, -1, -1, -1, -1,
+ 939, 788, 789, 790, -1, -1, -1, -1, -1, 796,
+ -1, -1, -1, -1, -1, 802, 803, -1, -1, -1,
+ -1, 808, 809, -1, -1, -1, 441, 442, -1, -1,
+ -1, -1, -1, 210, -1, 822, -1, -1, -1, -1,
+ -1, -1, -1, 458, -1, -1, -1, 834, 835, -1,
+ -1, -1, -1, -1, 469, 842, -1, -1, -1, -1,
+ -1, -1, 477, -1, -1, -1, -1, 854, 855, -1,
+ -1, -1, 487, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1029, -1, -1, 880, -1, -1, -1, -1, 885, -1,
+ -1, -1, -1, 2, 519, 4, -1, -1, -1, -1,
+ -1, 288, -1, 528, 13, -1, -1, -1, 295, 296,
+ -1, 908, -1, -1, -1, -1, 303, 542, -1, 544,
+ -1, 918, 919, -1, -1, 922, -1, 314, 553, 926,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 52, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 348, -1, -1, -1, -1, 353, -1, -1, -1,
+ -1, -1, -1, 13, 14, 15, 16, 17, 18, -1,
+ 20, 606, -1, -1, -1, 372, 26, 27, -1, 986,
+ -1, 988, -1, -1, 991, -1, -1, 37, 38, -1,
+ 40, 41, 42, 43, 44, -1, 393, -1, -1, -1,
+ 119, 398, 399, -1, 401, -1, 2, -1, 4, 5,
+ 6, 7, -1, -1, -1, -1, -1, 13, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 674,
+ 90, -1, -1, -1, 441, 442, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 105, 52, -1, -1, -1,
+ 56, 458, -1, -1, -1, 115, -1, -1, 118, 119,
+ -1, -1, 469, -1, -1, -1, -1, -1, -1, -1,
+ 477, -1, -1, -1, -1, -1, 82, -1, 138, 139,
+ 487, 210, -1, -1, 144, 145, 146, 147, 148, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 519, 119, -1, -1, -1, -1, -1, -1,
+ -1, 528, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 542, 781, 544, -1, -1,
+ -1, -1, -1, 788, 789, 790, 553, -1, -1, -1,
+ -1, 796, -1, -1, -1, -1, -1, 802, 803, 288,
+ -1, -1, -1, 808, 809, -1, 295, 296, -1, -1,
+ -1, -1, -1, -1, 303, -1, -1, 822, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 834,
+ 835, -1, -1, -1, -1, -1, -1, 842, -1, 606,
+ -1, -1, -1, -1, 210, -1, -1, -1, -1, 854,
+ 855, -1, -1, -1, -1, -1, -1, -1, -1, 348,
+ -1, -1, -1, -1, 353, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 880, -1, -1, -1, -1,
+ 885, -1, -1, 372, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, -1, 4, 908, 393, -1, -1, 674, -1, 398,
+ -1, -1, 401, 918, 919, -1, -1, 922, -1, -1,
+ -1, 926, 288, -1, -1, -1, -1, -1, -1, 295,
+ 296, -1, -1, -1, -1, -1, -1, 303, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 314, -1,
+ 52, -1, 441, 442, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 458,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 469, 986, 348, 988, -1, -1, 991, 353, 477, -1,
+ -1, 72, 73, 74, 75, 76, 77, 78, 487, 80,
+ 81, -1, -1, -1, -1, -1, 372, 88, 89, -1,
+ -1, -1, -1, -1, 781, -1, -1, 119, -1, -1,
+ 101, 788, 789, 790, -1, -1, -1, 393, -1, 796,
+ 519, -1, 398, 399, -1, 802, 803, -1, -1, 528,
+ -1, 808, 809, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, -1, -1, 822, -1, -1, -1, -1,
+ -1, -1, -1, -1, 553, -1, -1, 834, 835, -1,
+ -1, -1, -1, -1, -1, 842, -1, 72, 73, 74,
+ 75, 76, 77, -1, -1, 80, 81, 854, 855, -1,
+ -1, -1, 458, 88, 89, -1, -1, -1, -1, -1,
+ -1, -1, -1, 469, -1, -1, 101, -1, 210, -1,
+ -1, 477, -1, 880, -1, -1, -1, -1, 885, -1,
+ -1, 487, -1, -1, -1, -1, -1, -1, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, -1,
+ -1, 908, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 918, 919, 519, -1, 922, -1, -1, -1, 926,
+ -1, -1, 528, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 542, -1, 544, -1,
+ -1, -1, -1, -1, -1, 674, 288, 553, -1, -1,
+ -1, -1, -1, 295, 296, -1, -1, 44, -1, -1,
+ -1, 303, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 986,
+ -1, 988, -1, -1, 991, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, -1, -1, -1,
+ 606, 88, 89, -1, -1, -1, 348, -1, -1, -1,
+ -1, 353, -1, -1, 101, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 372, -1, -1, -1, -1, 122, -1, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, -1, -1, -1,
+ -1, 393, 781, -1, -1, 142, 398, -1, -1, 401,
+ 789, 790, -1, -1, -1, -1, -1, 796, 674, -1,
+ -1, -1, -1, 802, 803, -1, -1, -1, -1, 808,
+ 809, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 822, -1, -1, -1, -1, -1, 441,
+ 442, -1, -1, -1, -1, 834, 835, -1, -1, -1,
+ -1, -1, -1, 842, -1, -1, 458, -1, -1, -1,
+ -1, -1, -1, -1, -1, 854, 855, 469, -1, -1,
+ -1, -1, -1, -1, -1, 477, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 487, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 781, -1, 519, -1, 908,
+ -1, -1, 788, 789, -1, -1, 528, -1, -1, 918,
+ 919, -1, -1, 922, -1, -1, -1, 926, -1, -1,
+ -1, -1, 808, 809, -1, -1, -1, -1, -1, -1,
+ -1, 553, -1, -1, -1, -1, 822, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 834, 835,
+ -1, -1, -1, -1, -1, -1, 842, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 854, 855,
+ -1, -1, -1, -1, -1, -1, -1, 986, -1, 988,
+ -1, -1, 991, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 880, -1, -1, -1, -1, 885,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 13, 14,
+ 15, -1, 17, 18, -1, 20, -1, -1, -1, -1,
+ -1, 26, 918, 919, -1, -1, 922, -1, -1, -1,
+ 926, -1, 37, 38, -1, 40, 41, 42, 43, 44,
+ -1, -1, 674, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, -1,
+ -1, -1, -1, 88, 89, 90, -1, 92, 93, -1,
+ 986, -1, 988, -1, -1, 991, 101, -1, -1, -1,
+ 105, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 115, -1, -1, 118, 119, -1, 121, 122, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, -1,
+ -1, -1, -1, 138, 139, 140, -1, 142, -1, -1,
+ 145, 146, 147, 148, -1, -1, -1, -1, -1, 781,
+ -1, -1, -1, -1, -1, -1, -1, 789, 790, -1,
+ -1, -1, 51, 52, 796, -1, 55, -1, -1, -1,
+ 802, 803, -1, -1, -1, -1, 808, 809, -1, -1,
+ -1, 70, 71, 72, 73, 74, 75, 76, 77, -1,
+ 822, 80, 81, -1, -1, -1, -1, 86, 87, 88,
+ 89, -1, 834, 835, -1, -1, -1, -1, -1, -1,
+ 842, 100, 101, 102, -1, -1, -1, -1, -1, -1,
+ -1, -1, 854, 855, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, -1, 135, 136, 72, 73,
+ 74, 75, 76, 77, 143, 144, 80, 81, -1, -1,
+ -1, -1, -1, -1, 88, 89, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 908, 101, -1, -1,
+ -1, -1, -1, -1, -1, -1, 918, 919, -1, -1,
+ -1, -1, -1, -1, 926, -1, -1, -1, -1, -1,
+ 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+ -1, -1, -1, -1, -1, -1, 0, 1, -1, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, -1,
+ -1, -1, -1, -1, -1, 19, -1, 21, 22, 23,
+ 24, -1, -1, -1, -1, -1, 30, 31, 32, 33,
+ 34, 35, 36, -1, 986, 39, 988, -1, -1, 991,
+ -1, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, -1, 58, 59, 60, -1, -1, 63,
+ -1, -1, 66, 67, -1, 69, 70, 71, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 84, 85, -1, -1, -1, -1, -1, 91, -1, -1,
+ 94, 95, -1, 97, 98, -1, 100, -1, -1, -1,
+ 104, -1, 106, 107, 108, -1, 110, 111, 112, 0,
+ 114, 115, -1, -1, 118, 119, -1, -1, -1, -1,
+ -1, -1, 13, 14, 15, 16, 17, 18, -1, 20,
+ 134, 135, 136, -1, -1, -1, 27, 28, 29, -1,
+ -1, -1, 146, -1, 148, -1, 37, 38, -1, 40,
+ 41, 42, 43, 44, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 57, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, -1, -1, -1, -1, 88, 89, 90,
+ -1, -1, 93, -1, -1, -1, -1, -1, 99, -1,
+ 101, -1, -1, -1, 105, -1, -1, -1, -1, -1,
+ -1, -1, 113, -1, 115, -1, -1, 118, 119, -1,
+ -1, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, -1, -1, 0, -1, -1, 139, 140,
+ 141, 142, -1, -1, 145, 146, 147, 148, 13, 14,
+ 15, 16, 17, 18, -1, 20, -1, -1, -1, -1,
+ -1, 26, 27, 28, -1, -1, -1, -1, -1, -1,
+ -1, -1, 37, 38, -1, 40, 41, 42, 43, 44,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, -1,
+ -1, -1, -1, 88, 89, 90, -1, -1, 93, -1,
+ -1, -1, -1, -1, 99, -1, 101, -1, -1, -1,
+ 105, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 115, -1, -1, 118, 119, -1, -1, 122, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, -1,
+ -1, 0, -1, 138, 139, 140, 141, 142, -1, 144,
+ 145, 146, 147, 148, 13, 14, 15, 16, 17, 18,
+ -1, 20, -1, -1, -1, -1, -1, -1, 27, 28,
+ -1, -1, -1, -1, -1, -1, -1, -1, 37, 38,
+ -1, 40, 41, 42, 43, 44, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 57, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, -1, -1, -1, -1, 88,
+ 89, 90, -1, 92, 93, -1, -1, -1, -1, -1,
+ 99, -1, 101, -1, -1, -1, 105, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 115, -1, -1, 118,
+ 119, -1, 121, 122, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, -1, -1, 0, -1, -1,
+ 139, 140, 141, 142, -1, -1, 145, 146, 147, 148,
+ 13, 14, 15, 16, 17, 18, -1, 20, -1, -1,
+ -1, -1, -1, 26, 27, 28, -1, -1, -1, -1,
+ -1, -1, -1, -1, 37, 38, -1, 40, 41, 42,
+ 43, 44, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, -1, -1, -1, -1, 88, 89, 90, -1, -1,
+ 93, -1, -1, -1, -1, -1, 99, -1, 101, -1,
+ -1, -1, 105, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 115, -1, -1, 118, 119, -1, -1, 122,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, -1, -1, 0, -1, 138, 139, 140, 141, 142,
+ -1, 144, 145, 146, 147, 148, 13, 14, 15, 16,
+ 17, 18, -1, 20, -1, -1, -1, -1, -1, -1,
+ 27, 28, -1, -1, -1, -1, -1, -1, -1, -1,
+ 37, 38, -1, 40, 41, 42, 43, 44, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, -1, -1, -1,
+ -1, 88, 89, 90, -1, -1, 93, -1, -1, -1,
+ -1, -1, 99, -1, 101, -1, -1, -1, 105, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 115, -1,
+ -1, 118, 119, -1, -1, 122, -1, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, -1, -1, 0,
+ -1, -1, 139, 140, 141, 142, -1, 144, 145, 146,
+ 147, 148, 13, 14, 15, -1, 17, 18, -1, 20,
+ -1, -1, -1, -1, -1, 26, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 37, 38, -1, 40,
+ 41, 42, 43, 44, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, -1, -1, -1, -1, 88, 89, 90,
+ -1, 92, 93, -1, -1, -1, -1, -1, -1, -1,
+ 101, -1, -1, -1, 105, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 115, -1, -1, 118, 119, -1,
+ 121, 122, -1, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, -1, -1, 0, -1, 138, 139, 140,
+ -1, 142, -1, -1, 145, 146, 147, 148, 13, 14,
+ 15, -1, 17, 18, -1, 20, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 37, 38, -1, 40, 41, 42, 43, 44,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, -1,
+ -1, -1, -1, 88, 89, 90, -1, 92, 93, -1,
+ -1, -1, -1, -1, -1, -1, 101, -1, -1, -1,
+ 105, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 115, -1, -1, 118, 119, -1, 121, 122, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, -1,
+ -1, 0, -1, -1, 139, 140, -1, 142, -1, -1,
+ 145, 146, 147, 148, 13, 14, 15, -1, 17, 18,
+ -1, 20, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 37, 38,
+ -1, 40, 41, 42, 43, 44, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, -1, -1, -1, -1, 88,
+ 89, 90, -1, 92, 93, -1, -1, -1, -1, -1,
+ -1, -1, 101, -1, -1, -1, 105, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 115, -1, -1, 118,
+ 119, -1, 121, 122, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, -1, -1, -1, -1, -1,
+ 139, 140, -1, 142, -1, -1, 145, 146, 147, 148,
+ 1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, -1, -1, 18, 19, -1,
+ 21, 22, 23, 24, -1, -1, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, 45, -1, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, 100,
+ -1, -1, -1, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, 115, -1, -1, 118, 119, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 134, 135, 136, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 146, 1, 148, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, -1, -1,
+ 15, -1, 17, 18, 19, -1, 21, 22, 23, 24,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ 45, -1, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, 70, 71, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 84,
+ 85, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, 100, -1, -1, -1, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ 115, -1, -1, 118, 119, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 134,
+ 135, 136, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 146, 1, 148, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, -1, -1, 15, -1, -1, 18,
+ 19, 20, 21, 22, 23, 24, -1, -1, -1, -1,
+ -1, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, 45, -1, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, 70, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, 100, -1, -1, -1, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, 115, -1, -1, 118,
+ 119, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 134, 135, 136, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 146, 1, 148,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ -1, -1, 15, -1, -1, 18, 19, -1, 21, 22,
+ 23, 24, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, 45, -1, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, 70, 71, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 84, 85, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, 100, -1, -1,
+ -1, 104, -1, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, 115, -1, -1, 118, 119, 1, -1, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, -1,
+ -1, 134, 135, 136, -1, 19, -1, 21, 22, 23,
+ 24, -1, -1, 146, -1, 148, 30, 31, 32, 33,
+ 34, 35, 36, -1, -1, 39, -1, -1, -1, -1,
+ -1, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, -1, 58, 59, 60, -1, -1, 63,
+ -1, -1, 66, 67, -1, 69, 70, 71, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 84, 85, -1, -1, -1, -1, -1, 91, -1, -1,
+ 94, 95, -1, 97, 98, -1, 100, -1, -1, -1,
+ 104, -1, 106, 107, 108, -1, 110, 111, 112, -1,
+ 114, 115, -1, -1, 118, 119, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 134, 135, 136, -1, -1, 139, -1, -1, -1, -1,
+ -1, -1, 146, 1, 148, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, -1, 14, 15, -1, -1,
+ -1, 19, -1, 21, 22, 23, 24, -1, -1, -1,
+ -1, -1, 30, 31, 32, 33, 34, 35, 36, -1,
+ -1, 39, -1, -1, -1, -1, -1, 45, -1, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, -1,
+ 58, 59, 60, -1, -1, 63, -1, -1, 66, 67,
+ -1, 69, 70, 71, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 84, 85, -1, -1,
+ -1, -1, -1, 91, -1, -1, 94, 95, -1, 97,
+ 98, -1, 100, -1, -1, -1, 104, -1, 106, 107,
+ 108, -1, 110, 111, 112, -1, 114, 115, -1, -1,
+ 118, 119, 1, -1, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, -1, -1, 134, 135, 136, -1,
+ 19, -1, 21, 22, 23, 24, -1, -1, 146, -1,
+ 148, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, 45, -1, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, 70, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, 100, -1, -1, -1, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, 115, -1, -1, 118,
+ 119, 1, -1, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, -1, -1, 134, 135, 136, -1, 19,
+ -1, 21, 22, 23, 24, -1, 145, 146, -1, 148,
+ 30, 31, 32, 33, 34, 35, 36, -1, -1, 39,
+ -1, -1, -1, -1, -1, 45, -1, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, -1, 58, 59,
+ 60, -1, -1, 63, -1, -1, 66, 67, -1, 69,
+ 70, 71, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 84, 85, -1, -1, -1, -1,
+ -1, 91, -1, -1, 94, 95, -1, 97, 98, -1,
+ 100, -1, -1, -1, 104, -1, 106, 107, 108, -1,
+ 110, 111, 112, -1, 114, 115, -1, -1, 118, 119,
+ 1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, -1, -1, 134, 135, 136, -1, 19, -1,
+ 21, 22, 23, 24, -1, 145, 146, -1, 148, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, 45, -1, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, 100,
+ -1, -1, -1, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, 115, -1, -1, 118, 119, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 134, 135, 136, -1, -1, 139, -1,
+ -1, -1, -1, -1, -1, 146, 1, 148, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, -1, -1,
+ 15, -1, -1, -1, 19, -1, 21, 22, 23, 24,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ 45, -1, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, 70, 71, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 84,
+ 85, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, 100, -1, -1, -1, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ 115, -1, -1, 118, 119, -1, -1, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, -1, -1, 134,
+ 135, 136, -1, 19, -1, 21, 22, 23, 24, -1,
+ -1, 146, -1, 148, 30, 31, 32, 33, 34, 35,
+ 36, -1, -1, 39, -1, -1, -1, -1, -1, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, -1, 58, 59, 60, -1, -1, 63, -1, -1,
+ 66, 67, -1, 69, 70, 71, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 84, 85,
+ -1, -1, -1, -1, -1, 91, -1, -1, 94, 95,
+ -1, 97, 98, -1, 100, -1, -1, -1, 104, -1,
+ 106, 107, 108, -1, 110, 111, 112, -1, 114, 115,
+ -1, -1, 118, 119, -1, -1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, -1, -1, 134, 135,
+ 136, -1, 19, -1, 21, 22, 23, 24, -1, -1,
+ 146, -1, 148, 30, 31, 32, 33, 34, 35, 36,
+ -1, -1, 39, -1, -1, -1, -1, -1, 45, -1,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ -1, 58, 59, 60, -1, -1, 63, -1, -1, 66,
+ 67, -1, 69, 70, 71, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 84, 85, -1,
+ -1, -1, -1, -1, 91, -1, -1, 94, 95, -1,
+ 97, 98, -1, 100, -1, -1, -1, 104, -1, 106,
+ 107, 108, -1, 110, 111, 112, -1, 114, 115, -1,
+ -1, 118, 119, -1, -1, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, -1, -1, -1, 134, 135, 136,
+ -1, 19, -1, 21, 22, 23, 24, -1, -1, 146,
+ -1, 148, 30, 31, 32, 33, 34, 35, 36, -1,
+ -1, 39, -1, -1, -1, -1, -1, -1, -1, -1,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, -1,
+ 58, 59, 60, -1, -1, 63, -1, -1, 66, 67,
+ -1, 69, 70, 71, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 84, 85, -1, -1,
+ -1, -1, -1, 91, -1, -1, 94, 95, -1, 97,
+ 98, -1, -1, -1, -1, -1, 104, -1, 106, 107,
+ 108, -1, 110, 111, 112, -1, 114, 115, -1, -1,
+ 118, 119, -1, -1, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, -1, -1, -1, 134, 135, 136, -1,
+ 19, -1, 21, 22, 23, 24, -1, -1, 146, -1,
+ 148, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, 70, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, -1, -1, -1, -1, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, 115, -1, -1, 118,
+ 119, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 134, 135, 136, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 146, -1, 148,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, -1, -1, -1,
+ -1, -1, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 70, 71, 72,
+ 73, 74, 75, 76, 77, -1, -1, 80, 81, -1,
+ -1, -1, -1, 86, 87, 88, 89, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 100, 101, 102,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, -1, 135, 136, -1, -1, -1, -1, -1, -1,
+ 143, 144, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, -1, -1, -1, -1, -1, -1, -1, 19, -1,
+ 21, 22, 23, 24, -1, 26, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, -1, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, 100,
+ -1, 102, 103, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 134, 135, 136, -1, 138, -1, -1,
+ -1, -1, -1, 144, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, -1, -1, -1, -1, -1, -1, -1,
+ 19, -1, 21, 22, 23, 24, -1, 26, -1, -1,
+ -1, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, 70, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, 100, -1, 102, 103, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, -1, -1, -1, -1,
+ -1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, -1, -1, -1, -1, 134, 135, 136, 19, 138,
+ 21, 22, 23, 24, -1, 144, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, -1, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, 92, -1, 94, 95, -1, 97, 98, -1, 100,
+ -1, 102, 103, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, -1, -1, -1, -1, -1, -1,
+ 121, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ -1, -1, -1, 134, 135, 136, -1, 19, -1, 21,
+ 22, 23, 24, 144, -1, -1, -1, -1, 30, 31,
+ 32, 33, 34, 35, 36, -1, -1, 39, -1, -1,
+ -1, -1, -1, -1, -1, -1, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, -1, 58, 59, 60, -1,
+ -1, 63, -1, -1, 66, 67, -1, 69, 70, 71,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 84, 85, -1, -1, -1, -1, -1, 91,
+ 92, -1, 94, 95, -1, 97, 98, -1, 100, -1,
+ 102, 103, 104, -1, 106, 107, 108, -1, 110, 111,
+ 112, -1, 114, -1, -1, -1, -1, -1, -1, 121,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, -1,
+ -1, -1, 134, 135, 136, -1, 19, -1, 21, 22,
+ 23, 24, 144, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, -1, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, 70, 71, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 84, 85, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, 100, -1, 102,
+ 103, 104, -1, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, -1, -1, -1,
+ -1, 134, 135, 136, 19, -1, 21, 22, 23, 24,
+ -1, 144, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, 70, 71, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 84,
+ 85, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, 100, -1, 102, 103, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 134,
+ 135, 136, -1, -1, -1, -1, -1, -1, -1, 144,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, -1, -1, -1,
+ -1, -1, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, -1, -1, -1, -1, -1,
+ 63, -1, -1, -1, -1, -1, -1, 70, 71, 72,
+ 73, 74, 75, 76, 77, -1, -1, 80, 81, -1,
+ -1, -1, -1, 86, 87, 88, 89, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 100, 101, 102,
+ -1, -1, -1, -1, 107, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, -1, 135, 136, -1, -1, -1, -1, -1, -1,
+ 143, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, -1, -1, -1, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, -1, -1,
+ -1, -1, -1, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, -1, -1, -1, -1, -1,
+ -1, 63, -1, -1, -1, -1, -1, -1, 70, 71,
+ 72, 73, 74, 75, 76, 77, -1, -1, 80, 81,
+ -1, -1, -1, -1, 86, 87, 88, 89, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 100, 101,
+ 102, -1, -1, -1, -1, 107, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, -1, 135, 136, -1, -1, -1, -1, -1,
+ -1, 143, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, -1,
+ -1, -1, -1, -1, 45, 46, 47, 48, 49, 50,
+ 51, 52, -1, -1, 55, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 70,
+ 71, 72, 73, 74, 75, 76, 77, -1, -1, 80,
+ 81, -1, -1, -1, -1, 86, 87, 88, 89, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 100,
+ 101, 102, -1, -1, -1, 106, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, -1, 135, 136, -1, -1, -1, -1,
+ -1, -1, 143, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, -1, -1, -1,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ -1, -1, -1, -1, -1, 45, 46, 47, 48, 49,
+ 50, 51, 52, -1, -1, 55, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 70, 71, 72, 73, 74, 75, 76, 77, -1, -1,
+ 80, 81, -1, -1, -1, -1, 86, 87, 88, 89,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 100, 101, 102, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, -1, 135, 136, -1, -1, -1,
+ -1, -1, -1, 143, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, -1, -1, -1, -1, -1, -1, -1,
+ 19, -1, 21, 22, 23, 24, -1, -1, -1, -1,
+ -1, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, -1, -1, -1, -1, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, -1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, -1, -1, -1,
+ -1, -1, -1, -1, 19, 134, 21, 22, 23, 24,
+ -1, -1, -1, 142, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, -1, -1, -1, -1, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ -1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, -1, -1, -1, -1, -1, -1, 19, 134,
+ 21, 22, 23, 24, -1, -1, -1, 142, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, 100,
+ -1, -1, -1, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ -1, -1, -1, 134, 135, 136, 19, -1, 21, 22,
+ 23, 24, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, 45, -1, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, 70, 71, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 84, 85, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, 100, -1, -1,
+ -1, 104, -1, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, -1, -1, -1,
+ -1, 134, 135, 136, 19, -1, 21, 22, 23, 24,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, 70, 71, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 84,
+ 85, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, 100, -1, 102, 103, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ -1, -1, -1, -1, -1, -1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, -1, -1, -1, -1, 134,
+ 135, 136, 19, -1, 21, 22, 23, 24, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, -1, 39, -1, -1, -1, -1, -1, -1, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ -1, 58, 59, 60, -1, -1, 63, -1, -1, 66,
+ 67, -1, 69, 70, 71, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 84, 85, -1,
+ -1, -1, -1, -1, 91, -1, -1, 94, 95, -1,
+ 97, 98, -1, 100, -1, 102, 103, 104, -1, 106,
+ 107, 108, -1, 110, 111, 112, -1, 114, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, -1, -1, -1, -1, 134, 135, 136,
+ 19, -1, 21, 22, 23, 24, -1, -1, -1, -1,
+ -1, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, 70, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, 100, -1, 102, 103, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, -1, -1, -1, -1,
+ -1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, -1, -1, -1, -1, 134, 135, 136, 19, -1,
+ 21, 22, 23, 24, -1, -1, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, -1, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, 100,
+ -1, 102, 103, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, -1,
+ -1, -1, -1, 134, 135, 136, 19, -1, 21, 22,
+ 23, 24, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, -1, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, 70, 71, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 84, 85, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, 100, -1, 102,
+ -1, 104, -1, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, -1, -1, -1,
+ -1, 134, 135, 136, 19, -1, 21, 22, 23, 24,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, 70, 71, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 84,
+ 85, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, -1, -1, 102, 103, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ -1, -1, -1, -1, -1, -1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, -1, -1, -1, -1, 134,
+ 135, 136, 19, -1, 21, 22, 23, 24, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, -1, 39, -1, -1, -1, -1, -1, -1, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ -1, 58, 59, 60, -1, -1, 63, -1, -1, 66,
+ 67, -1, 69, 70, 71, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 84, 85, -1,
+ -1, -1, -1, -1, 91, -1, -1, 94, 95, -1,
+ 97, 98, -1, 100, -1, 102, -1, 104, -1, 106,
+ 107, 108, -1, 110, 111, 112, -1, 114, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, -1, -1, -1, -1, 134, 135, 136,
+ 19, -1, 21, 22, 23, 24, -1, -1, -1, -1,
+ -1, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, 70, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, -1, -1, 102, -1, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, -1, -1, -1, -1,
+ -1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, -1, -1, -1, -1, 134, 135, 136, 19, -1,
+ 21, 22, 23, 24, -1, -1, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, -1, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, 100,
+ -1, -1, -1, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, -1,
+ -1, -1, -1, 134, 135, 136, 19, -1, 21, 22,
+ 23, 24, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, -1, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, 70, 71, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 84, 85, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, 100, -1, -1,
+ -1, 104, -1, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, -1, -1, -1,
+ -1, 134, 135, 136, 19, -1, 21, 22, 23, 24,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, 70, 71, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 84,
+ 85, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, 100, -1, -1, -1, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ -1, -1, -1, -1, -1, -1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, -1, -1, -1, -1, 134,
+ 135, 136, 19, -1, 21, 22, 23, 24, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, -1, 39, -1, -1, -1, -1, -1, -1, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ -1, 58, 59, 60, -1, -1, 63, -1, -1, 66,
+ 67, -1, 69, 70, 71, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 84, 85, -1,
+ -1, -1, -1, -1, 91, -1, -1, 94, 95, -1,
+ 97, 98, -1, 100, -1, -1, -1, 104, -1, 106,
+ 107, 108, -1, 110, 111, 112, -1, 114, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, -1, -1, -1, -1, 134, 135, 136,
+ 19, -1, 21, 22, 23, 24, -1, -1, -1, -1,
+ -1, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, 70, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, 100, -1, -1, -1, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, -1, -1, -1, -1,
+ -1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, -1, -1, -1, -1, 134, 135, 136, 19, -1,
+ 21, 22, 23, 24, -1, -1, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, -1, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, 70,
+ 71, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 84, 85, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, -1,
+ -1, -1, -1, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, -1,
+ -1, -1, -1, 134, 135, 136, 19, -1, 21, 22,
+ 23, 24, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, -1, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, 70, 71, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 84, 85, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, -1, -1, -1,
+ -1, 104, -1, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, -1, -1, -1,
+ -1, 134, 135, 136, 19, -1, 21, 22, 23, 24,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, 70, 71, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 84,
+ 85, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, -1, -1, -1, -1, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ -1, -1, -1, -1, -1, -1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, -1, -1, -1, -1, 134,
+ 135, 136, 19, -1, 21, 22, 23, 24, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, -1, 39, -1, -1, -1, -1, -1, -1, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ -1, 58, 59, 60, -1, -1, 63, -1, -1, 66,
+ 67, -1, 69, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 88, -1, -1, 91, -1, -1, 94, 95, -1,
+ 97, 98, -1, -1, -1, -1, -1, 104, -1, 106,
+ 107, 108, -1, 110, 111, 112, -1, 114, -1, -1,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, -1,
+ -1, -1, -1, -1, -1, -1, 19, 134, 21, 22,
+ 23, 24, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, -1, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, 100, -1, -1,
+ -1, 104, -1, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, -1, -1, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, -1, -1, -1, -1, -1, -1, -1,
+ 19, 134, 21, 22, 23, 24, -1, -1, -1, -1,
+ -1, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, -1, 58,
+ 59, 60, -1, -1, 63, -1, -1, 66, 67, -1,
+ 69, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 95, -1, 97, 98,
+ -1, 100, -1, -1, -1, 104, -1, 106, 107, 108,
+ -1, 110, 111, 112, -1, 114, -1, -1, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, -1, -1, -1,
+ -1, -1, -1, -1, 19, 134, 21, 22, 23, 24,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, -1, 39, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, 58, 59, 60, -1, -1, 63, -1,
+ -1, 66, 67, -1, 69, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 91, -1, -1, 94,
+ 95, -1, 97, 98, -1, -1, -1, -1, -1, 104,
+ -1, 106, 107, 108, -1, 110, 111, 112, -1, 114,
+ -1, -1, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, -1, -1, -1, -1, -1, -1, -1, 19, 134,
+ 21, 22, 23, 24, -1, -1, -1, -1, -1, 30,
+ 31, 32, 33, 34, 35, 36, -1, -1, 39, -1,
+ -1, -1, -1, -1, -1, -1, -1, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, 63, -1, -1, 66, 67, -1, 69, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, -1, -1, 94, 95, -1, 97, 98, -1, -1,
+ -1, -1, -1, 104, -1, 106, 107, 108, -1, 110,
+ 111, 112, -1, 114, -1, -1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, -1, -1, -1, -1, -1,
+ -1, -1, 19, 134, 21, 22, 23, 24, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, -1, 39, -1, -1, -1, -1, -1, -1, -1,
+ -1, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ -1, 58, 59, 60, -1, -1, 63, -1, -1, 66,
+ 67, -1, 69, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 91, -1, -1, 94, 95, -1,
+ 97, 98, -1, -1, -1, -1, -1, 104, -1, 106,
+ 107, 108, -1, 110, 111, 112, -1, 114, -1, -1,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, -1,
+ -1, -1, -1, -1, -1, -1, 19, 134, 21, 22,
+ 23, 24, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, -1, 39, -1, -1, -1,
+ -1, -1, -1, -1, -1, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ 63, -1, -1, 66, 67, -1, 69, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 91, -1,
+ -1, 94, 95, -1, 97, 98, -1, -1, 51, 52,
+ -1, 104, 55, 106, 107, 108, -1, 110, 111, 112,
+ -1, 114, -1, -1, -1, -1, -1, 70, 71, 72,
+ 73, 74, 75, 76, 77, -1, -1, 80, 81, -1,
+ -1, 134, -1, 86, 87, 88, 89, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 100, 101, 102,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, -1, 135, 136, 51, 52, -1, -1, 55, -1,
+ 143, 144, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 70, 71, 72, 73, 74, 75, 76,
+ 77, -1, -1, 80, 81, -1, -1, -1, -1, 86,
+ 87, 88, 89, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 100, 101, 102, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, -1, 135, 136,
+ 51, 52, -1, -1, 55, -1, 143, 144, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 70,
+ 71, 72, 73, 74, 75, 76, 77, -1, -1, 80,
+ 81, -1, -1, -1, -1, 86, 87, 88, 89, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 100,
+ 101, 102, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, -1, 135, 136, 51, 52, -1, -1,
+ 55, -1, 143, 144, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 70, 71, 72, 73, 74,
+ 75, 76, 77, -1, -1, 80, 81, -1, -1, -1,
+ -1, 86, 87, 88, 89, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 100, 101, 102, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, -1,
+ 135, 136, 51, 52, -1, -1, 55, -1, 143, 144,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 70, 71, 72, 73, 74, 75, 76, 77, -1,
+ -1, 80, 81, -1, -1, -1, -1, 86, 87, 88,
+ 89, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 100, 101, 102, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, -1, 135, 136, 51, 52,
+ -1, -1, 55, -1, 143, 144, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 70, 71, 72,
+ 73, 74, 75, 76, 77, -1, -1, 80, 81, -1,
+ -1, -1, -1, 86, 87, 88, 89, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 100, 101, 102,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, -1, 135, 136, 51, 52, -1, -1, 55, -1,
+ 143, 144, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 70, 71, 72, 73, 74, 75, 76,
+ 77, -1, -1, 80, 81, -1, -1, -1, -1, 86,
+ 87, 88, 89, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 100, 101, 102, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, -1, 135, 136,
+ 51, 52, -1, -1, 55, -1, 143, 144, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 70,
+ 71, 72, 73, 74, 75, 76, 77, -1, -1, 80,
+ 81, -1, -1, -1, -1, 86, 87, 88, 89, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 100,
+ 101, 102, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, -1, 135, 136, 51, 52, -1, -1,
+ 55, -1, 143, 144, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 70, 71, 72, 73, 74,
+ 75, 76, 77, -1, -1, 80, 81, -1, -1, -1,
+ -1, 86, 87, 88, 89, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 100, 101, 102, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, -1,
+ 135, 136, 51, 52, -1, -1, 55, -1, 143, 144,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 70, 71, 72, 73, 74, 75, 76, 77, -1,
+ -1, 80, 81, -1, -1, -1, -1, 86, 87, 88,
+ 89, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 100, 101, 102, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, -1, 135, 136, 51, 52,
+ -1, -1, 55, -1, 143, 144, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 70, 71, 72,
+ 73, 74, 75, 76, 77, -1, -1, 80, 81, -1,
+ -1, -1, -1, 86, 87, 88, 89, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 100, 101, 102,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, -1, 135, 136, 51, 52, -1, -1, 55, -1,
+ 143, 144, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 70, 71, 72, 73, 74, 75, 76,
+ 77, -1, -1, 80, 81, -1, -1, -1, -1, 86,
+ 87, 88, 89, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 100, 101, 102, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, -1, 135, 136,
+ 51, 52, -1, -1, 55, -1, 143, 144, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 70,
+ 71, 72, 73, 74, 75, 76, 77, -1, -1, 80,
+ 81, -1, -1, -1, -1, 86, 87, 88, 89, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 100,
+ 101, 102, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, -1, 135, 136, 51, 52, -1, -1,
+ 55, -1, 143, 144, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 70, 71, 72, 73, 74,
+ 75, 76, 77, -1, -1, 80, 81, -1, -1, -1,
+ -1, 86, 87, 88, 89, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 100, 101, 102, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, -1,
+ 135, 136, 51, 52, -1, -1, 55, -1, 143, 144,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 70, 71, 72, 73, 74, 75, 76, 77, -1,
+ -1, 80, 81, -1, -1, -1, -1, 86, 87, 88,
+ 89, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 100, 101, 102, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 44, -1, -1,
+ -1, -1, -1, -1, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, -1, 135, 136, -1, -1,
+ -1, -1, -1, -1, 143, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, -1, -1, -1,
+ -1, 88, 89, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 101, -1, -1, -1, -1, 44,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 122, -1, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, -1,
+ -1, -1, -1, 88, 89, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 101, -1, -1, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 122, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, -1, -1, -1, -1, 88, 89, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 101, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 122,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, -1, -1, -1, -1, 88, 89, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 101, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 122, -1, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, -1, -1, 148, -1, 88,
+ 89, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 101, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 122, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, -1, -1, -1, -1, -1,
+ -1, -1, -1, 142, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 83, -1, -1, -1, -1,
+ 88, 89, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 101, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 122, -1, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, -1, -1, -1, -1,
+ -1, -1, -1, -1, 142, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, -1, -1, -1,
+ -1, 88, 89, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 101, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 122, -1, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, -1, -1, -1,
+ -1, -1, -1, -1, -1, 142, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, -1, -1,
+ -1, -1, 88, 89, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 101, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 122, -1, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, -1, -1,
+ -1, -1, -1, -1, -1, -1, 142, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, -1,
+ -1, -1, -1, 88, 89, -1, -1, -1, 93, -1,
+ -1, -1, -1, -1, -1, -1, 101, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 122, -1, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, -1, -1, -1, -1, 88, 89, -1, -1, -1,
+ 93, -1, -1, -1, -1, -1, -1, -1, 101, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 122,
+ -1, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, -1, -1, -1, -1, 88, 89, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 101, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 122, -1, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, -1, -1, -1, -1, 88,
+ 89, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 101, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133
+};
+
+ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_int16 yystos[] =
+{
+ 0, 150, 151, 0, 1, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 19, 21, 22, 23, 24,
+ 30, 31, 32, 33, 34, 35, 36, 39, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 58, 59, 60, 63, 66, 67, 69, 70, 71, 84,
+ 85, 91, 94, 95, 97, 98, 100, 104, 106, 107,
+ 108, 110, 111, 112, 114, 134, 135, 136, 152, 153,
+ 154, 159, 161, 163, 164, 165, 168, 169, 172, 173,
+ 175, 176, 177, 179, 180, 189, 203, 220, 241, 242,
+ 252, 253, 254, 258, 259, 260, 266, 267, 268, 270,
+ 271, 272, 273, 274, 275, 312, 325, 154, 21, 22,
+ 30, 31, 32, 39, 51, 55, 69, 88, 91, 94,
+ 134, 164, 165, 181, 182, 203, 220, 272, 275, 312,
+ 182, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 45, 46, 47, 48, 49,
+ 50, 51, 52, 55, 70, 71, 72, 73, 74, 75,
+ 76, 77, 80, 81, 86, 87, 88, 89, 100, 101,
+ 102, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 135, 136, 143, 144, 183, 187, 188, 274, 306,
+ 204, 91, 163, 164, 165, 167, 180, 189, 220, 272,
+ 273, 275, 167, 210, 212, 69, 91, 173, 180, 220,
+ 225, 272, 275, 33, 34, 35, 36, 48, 49, 50,
+ 51, 55, 106, 183, 184, 185, 268, 115, 118, 119,
+ 146, 148, 167, 262, 263, 264, 318, 322, 323, 324,
+ 51, 100, 102, 103, 135, 172, 189, 195, 198, 201,
+ 254, 309, 311, 195, 195, 144, 192, 193, 196, 197,
+ 325, 192, 196, 144, 319, 323, 184, 155, 138, 189,
+ 220, 189, 189, 189, 55, 1, 94, 157, 158, 159,
+ 174, 175, 325, 205, 207, 190, 201, 309, 325, 189,
+ 308, 309, 325, 91, 142, 179, 220, 272, 275, 208,
+ 53, 54, 56, 63, 107, 183, 269, 63, 64, 65,
+ 116, 117, 255, 256, 61, 255, 62, 255, 63, 255,
+ 63, 255, 58, 59, 168, 189, 189, 318, 324, 40,
+ 41, 42, 43, 44, 37, 38, 51, 53, 54, 55,
+ 56, 69, 94, 100, 101, 102, 103, 128, 131, 144,
+ 278, 279, 280, 281, 282, 285, 286, 287, 288, 290,
+ 291, 292, 293, 295, 296, 297, 300, 301, 302, 303,
+ 304, 325, 278, 280, 28, 239, 121, 142, 94, 100,
+ 176, 121, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 88, 89, 93, 101, 122, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 90,
+ 105, 140, 147, 316, 90, 316, 317, 26, 138, 243,
+ 254, 92, 92, 192, 196, 243, 163, 51, 55, 181,
+ 58, 59, 279, 125, 276, 90, 140, 316, 219, 307,
+ 90, 147, 315, 156, 157, 55, 278, 278, 16, 221,
+ 322, 121, 90, 140, 316, 92, 92, 221, 167, 167,
+ 55, 90, 140, 316, 25, 107, 142, 265, 318, 115,
+ 264, 20, 246, 322, 57, 310, 189, 189, 189, 93,
+ 142, 199, 200, 325, 310, 199, 200, 85, 194, 195,
+ 201, 309, 325, 195, 163, 318, 320, 163, 160, 138,
+ 157, 90, 316, 92, 159, 174, 145, 318, 324, 320,
+ 159, 320, 141, 200, 321, 324, 200, 321, 139, 321,
+ 55, 176, 177, 178, 142, 90, 140, 316, 144, 237,
+ 290, 63, 255, 257, 261, 262, 63, 256, 61, 62,
+ 63, 63, 101, 101, 154, 167, 167, 167, 167, 159,
+ 163, 163, 57, 121, 294, 85, 290, 295, 121, 156,
+ 189, 142, 305, 325, 51, 142, 305, 322, 142, 289,
+ 189, 142, 289, 51, 142, 289, 51, 121, 156, 240,
+ 100, 168, 189, 201, 202, 174, 142, 179, 142, 161,
+ 162, 168, 180, 189, 191, 202, 220, 275, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 51, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 51, 52, 55, 187, 192,
+ 313, 314, 194, 201, 51, 52, 55, 187, 192, 313,
+ 51, 55, 313, 245, 244, 162, 189, 191, 162, 191,
+ 99, 170, 217, 277, 216, 51, 55, 181, 313, 194,
+ 313, 156, 163, 166, 15, 13, 248, 325, 121, 121,
+ 157, 16, 51, 55, 194, 51, 55, 157, 27, 222,
+ 322, 222, 51, 55, 194, 51, 55, 214, 186, 157,
+ 246, 189, 201, 15, 261, 189, 189, 319, 100, 189,
+ 198, 309, 189, 311, 320, 145, 318, 200, 200, 320,
+ 145, 184, 152, 139, 191, 320, 159, 206, 309, 176,
+ 178, 51, 55, 194, 51, 55, 290, 209, 63, 157,
+ 262, 189, 189, 51, 100, 226, 295, 320, 320, 142,
+ 172, 189, 15, 51, 282, 287, 304, 288, 293, 300,
+ 302, 295, 297, 302, 51, 295, 172, 189, 15, 79,
+ 126, 231, 232, 325, 189, 200, 320, 178, 142, 44,
+ 121, 44, 90, 140, 316, 319, 92, 92, 192, 196,
+ 141, 200, 92, 92, 193, 196, 193, 196, 231, 231,
+ 171, 322, 167, 156, 141, 15, 320, 183, 189, 202,
+ 249, 325, 18, 224, 325, 17, 223, 224, 92, 92,
+ 141, 92, 92, 224, 211, 213, 141, 167, 184, 139,
+ 15, 200, 221, 261, 189, 199, 85, 309, 139, 320,
+ 321, 141, 234, 319, 29, 113, 238, 139, 142, 292,
+ 320, 142, 85, 44, 44, 305, 142, 289, 142, 289,
+ 142, 289, 142, 289, 289, 44, 44, 228, 230, 233,
+ 281, 283, 284, 287, 295, 296, 298, 299, 302, 304,
+ 156, 100, 189, 178, 159, 189, 51, 55, 194, 51,
+ 55, 57, 123, 162, 191, 168, 191, 170, 92, 162,
+ 191, 162, 191, 170, 243, 239, 156, 157, 231, 218,
+ 322, 15, 93, 250, 325, 157, 14, 251, 325, 167,
+ 15, 92, 15, 157, 157, 222, 189, 157, 320, 200,
+ 145, 146, 156, 157, 227, 142, 100, 320, 189, 189,
+ 295, 302, 295, 295, 189, 189, 234, 234, 91, 220,
+ 142, 305, 305, 142, 229, 220, 142, 229, 142, 229,
+ 15, 189, 141, 189, 189, 162, 191, 15, 139, 157,
+ 156, 91, 180, 220, 272, 275, 221, 157, 221, 15,
+ 15, 215, 224, 246, 247, 51, 235, 236, 291, 15,
+ 139, 295, 295, 142, 292, 289, 142, 289, 289, 289,
+ 126, 126, 55, 90, 283, 287, 142, 228, 229, 299,
+ 302, 295, 298, 302, 295, 139, 15, 55, 90, 140,
+ 316, 157, 157, 157, 142, 319, 142, 295, 142, 295,
+ 51, 55, 305, 142, 229, 142, 229, 142, 229, 142,
+ 229, 229, 51, 55, 194, 51, 55, 248, 223, 15,
+ 236, 295, 289, 295, 302, 295, 295, 141, 229, 142,
+ 229, 229, 229, 295, 229
+};
+
+ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_int16 yyr1[] =
+{
+ 0, 149, 151, 150, 152, 153, 153, 153, 153, 154,
+ 155, 154, 156, 157, 158, 158, 158, 158, 160, 159,
+ 159, 159, 159, 159, 159, 159, 159, 159, 159, 159,
+ 159, 159, 159, 159, 161, 161, 161, 161, 161, 161,
+ 161, 161, 161, 161, 161, 161, 162, 162, 162, 163,
+ 163, 163, 163, 163, 163, 164, 166, 165, 167, 168,
+ 168, 169, 169, 171, 170, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 173, 173, 174, 174,
+ 175, 175, 175, 175, 175, 175, 175, 175, 175, 175,
+ 176, 176, 177, 177, 178, 178, 179, 179, 179, 179,
+ 179, 179, 179, 179, 180, 180, 180, 180, 180, 180,
+ 180, 180, 180, 181, 181, 182, 182, 182, 183, 183,
+ 183, 183, 183, 184, 184, 185, 186, 185, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189, 189, 190,
+ 190, 190, 190, 191, 191, 192, 192, 192, 193, 193,
+ 194, 194, 194, 194, 194, 195, 195, 195, 195, 195,
+ 197, 196, 198, 199, 199, 200, 200, 201, 201, 201,
+ 201, 202, 202, 202, 203, 203, 203, 203, 203, 203,
+ 203, 203, 203, 204, 203, 205, 206, 203, 207, 203,
+ 203, 203, 203, 203, 203, 203, 203, 203, 203, 203,
+ 203, 203, 208, 209, 203, 203, 203, 210, 211, 203,
+ 212, 213, 203, 203, 203, 214, 215, 203, 216, 203,
+ 217, 218, 203, 219, 203, 203, 203, 203, 203, 203,
+ 203, 220, 221, 221, 221, 222, 222, 223, 223, 224,
+ 224, 225, 225, 226, 226, 226, 226, 226, 226, 226,
+ 226, 227, 226, 228, 228, 228, 228, 229, 229, 230,
+ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 231, 231, 233, 232, 232, 232,
+ 234, 234, 235, 235, 236, 236, 237, 237, 238, 238,
+ 240, 239, 241, 241, 241, 241, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 244, 243, 245, 243, 246,
+ 247, 247, 248, 248, 249, 249, 249, 250, 250, 251,
+ 251, 252, 252, 252, 252, 253, 253, 254, 254, 254,
+ 254, 255, 255, 256, 257, 256, 256, 256, 258, 258,
+ 259, 259, 260, 261, 261, 262, 262, 263, 263, 264,
+ 265, 264, 266, 266, 267, 267, 268, 269, 269, 269,
+ 269, 269, 269, 270, 270, 271, 271, 271, 271, 272,
+ 272, 272, 272, 272, 273, 273, 274, 274, 274, 274,
+ 274, 274, 274, 274, 275, 275, 276, 277, 276, 278,
+ 278, 279, 279, 279, 280, 280, 281, 282, 282, 283,
+ 283, 284, 284, 285, 285, 286, 286, 287, 287, 288,
+ 288, 288, 288, 289, 289, 290, 290, 290, 290, 290,
+ 290, 290, 290, 290, 290, 290, 290, 290, 290, 290,
+ 291, 291, 291, 291, 291, 292, 292, 293, 294, 293,
+ 295, 295, 296, 297, 298, 299, 299, 300, 300, 301,
+ 301, 302, 302, 303, 303, 304, 305, 305, 306, 307,
+ 306, 308, 308, 309, 309, 310, 310, 311, 311, 311,
+ 311, 312, 312, 312, 313, 313, 313, 313, 314, 314,
+ 314, 315, 315, 316, 316, 317, 317, 318, 318, 319,
+ 319, 320, 321, 321, 321, 322, 322, 322, 323, 324,
+ 324, 325
+};
+
+ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
+static const yytype_int8 yyr2[] =
+{
+ 0, 2, 0, 2, 2, 1, 1, 3, 2, 1,
+ 0, 5, 4, 2, 1, 1, 3, 2, 0, 4,
+ 2, 3, 3, 3, 3, 3, 4, 1, 3, 3,
+ 3, 3, 3, 1, 3, 3, 6, 5, 5, 5,
+ 5, 4, 6, 4, 6, 3, 1, 3, 1, 1,
+ 3, 3, 3, 2, 1, 2, 0, 5, 1, 1,
+ 1, 1, 4, 0, 5, 2, 3, 4, 5, 4,
+ 5, 2, 2, 2, 2, 2, 1, 3, 1, 3,
+ 1, 2, 3, 5, 2, 4, 2, 4, 1, 3,
+ 1, 3, 2, 3, 1, 2, 1, 4, 3, 3,
+ 3, 3, 2, 1, 1, 4, 3, 3, 3, 3,
+ 2, 1, 1, 1, 1, 2, 1, 3, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 4, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 3,
+ 6, 5, 5, 5, 5, 4, 3, 3, 2, 2,
+ 3, 2, 2, 3, 3, 3, 3, 3, 3, 4,
+ 4, 2, 2, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 2, 2, 3, 3,
+ 3, 3, 6, 6, 4, 6, 4, 6, 1, 1,
+ 2, 4, 2, 1, 3, 3, 5, 3, 1, 1,
+ 1, 2, 2, 4, 2, 1, 2, 2, 4, 1,
+ 0, 2, 2, 2, 1, 1, 3, 1, 2, 3,
+ 4, 3, 4, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 0, 4, 0, 0, 5, 0, 3,
+ 3, 3, 2, 3, 3, 1, 2, 4, 3, 2,
+ 1, 2, 0, 0, 5, 6, 6, 0, 0, 7,
+ 0, 0, 7, 5, 4, 0, 0, 9, 0, 6,
+ 0, 0, 8, 0, 5, 4, 4, 1, 1, 1,
+ 1, 1, 1, 1, 2, 1, 1, 1, 5, 1,
+ 2, 1, 1, 1, 4, 6, 3, 5, 2, 4,
+ 1, 0, 4, 4, 2, 2, 1, 2, 0, 6,
+ 8, 4, 6, 4, 3, 6, 2, 4, 6, 2,
+ 4, 2, 4, 1, 1, 1, 0, 4, 1, 4,
+ 1, 4, 1, 3, 1, 1, 4, 1, 3, 3,
+ 0, 5, 2, 4, 5, 5, 2, 4, 4, 3,
+ 3, 3, 2, 1, 4, 0, 5, 0, 5, 5,
+ 1, 1, 6, 1, 1, 1, 1, 2, 1, 2,
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 2,
+ 3, 1, 2, 1, 0, 4, 1, 2, 2, 3,
+ 2, 3, 1, 1, 2, 1, 2, 1, 2, 1,
+ 0, 4, 2, 3, 1, 4, 2, 1, 1, 1,
+ 1, 1, 2, 2, 3, 1, 1, 2, 2, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 0, 4, 1,
+ 1, 3, 5, 3, 1, 2, 2, 2, 1, 2,
+ 1, 1, 3, 1, 3, 1, 1, 2, 1, 4,
+ 2, 2, 1, 2, 0, 6, 8, 4, 6, 4,
+ 6, 2, 4, 6, 2, 4, 2, 4, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 4,
+ 1, 3, 2, 2, 2, 1, 3, 1, 3, 1,
+ 1, 2, 1, 1, 1, 2, 2, 1, 1, 0,
+ 4, 1, 2, 1, 3, 1, 2, 3, 3, 3,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+ 1, 2, 0, 1, 1, 1, 1, 1, 1, 1,
+ 2, 0
+};
+
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+ do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (p, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+ while (0)
+
+/* Error token number */
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, p); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+
+/*-----------------------------------.
+| Print this symbol's value on YYO. |
+`-----------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, parser_state *p)
+{
+ FILE *yyoutput = yyo;
+ YYUSE (yyoutput);
+ YYUSE (p);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyo, yytoknum[yytype], *yyvaluep);
+# endif
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ YYUSE (yytype);
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+/*---------------------------.
+| Print this symbol on YYO. |
+`---------------------------*/
+
+static void
+yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, parser_state *p)
+{
+ YYFPRINTF (yyo, "%s %s (",
+ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+ yy_symbol_value_print (yyo, yytype, yyvaluep, p);
+ YYFPRINTF (yyo, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop)
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule, parser_state *p)
+{
+ int yylno = yyrline[yyrule];
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr,
+ yystos[+yyssp[yyi + 1 - yynrhs]],
+ &yyvsp[(yyi + 1) - (yynrhs)]
+ , p);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyssp, yyvsp, Rule, p); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S)))
+# else
+/* Return the length of YYSTR. */
+static YYPTRDIFF_T
+yystrlen (const char *yystr)
+{
+ YYPTRDIFF_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYPTRDIFF_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYPTRDIFF_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ else
+ goto append;
+
+ append:
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (yyres)
+ return yystpcpy (yyres, yystr) - yyres;
+ else
+ return yystrlen (yystr);
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg,
+ yy_state_t *yyssp, int yytoken)
+{
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULLPTR;
+ /* Arguments of yyformat: reported tokens (one for the "unexpected",
+ one per "expected"). */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Actual size of YYARG. */
+ int yycount = 0;
+ /* Cumulated lengths of YYARG. */
+ YYPTRDIFF_T yysize = 0;
+
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[+*yyssp];
+ YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+ yysize = yysize0;
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYPTRDIFF_T yysize1
+ = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+ if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+ yysize = yysize1;
+ else
+ return 2;
+ }
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ default: /* Avoid compiler warnings. */
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ {
+ /* Don't count the "%s"s in the final size, but reserve room for
+ the terminator. */
+ YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1;
+ if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)
+ yysize = yysize1;
+ else
+ return 2;
+ }
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ ++yyp;
+ ++yyformat;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, parser_state *p)
+{
+ YYUSE (yyvaluep);
+ YYUSE (p);
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ YYUSE (yytype);
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+int
+yyparse (parser_state *p)
+{
+/* The lookahead symbol. */
+int yychar;
+
+
+/* The semantic value of the lookahead symbol. */
+/* Default value used for initialization, for pacifying older GCCs
+ or non-GCC compilers. */
+YY_INITIAL_VALUE (static YYSTYPE yyval_default;)
+YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);
+
+ /* Number of syntax errors so far. */
+ int yynerrs;
+
+ yy_state_fast_t yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ 'yyss': related to states.
+ 'yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yy_state_t yyssa[YYINITDEPTH];
+ yy_state_t *yyss;
+ yy_state_t *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYPTRDIFF_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+ goto yysetstate;
+
+
+/*------------------------------------------------------------.
+| yynewstate -- push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+
+/*--------------------------------------------------------------------.
+| yysetstate -- set current state (the top of the stack) to yystate. |
+`--------------------------------------------------------------------*/
+yysetstate:
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+ YY_ASSERT (0 <= yystate && yystate < YYNSTATES);
+ YY_IGNORE_USELESS_CAST_BEGIN
+ *yyssp = YY_CAST (yy_state_t, yystate);
+ YY_IGNORE_USELESS_CAST_END
+
+ if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+#else
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYPTRDIFF_T yysize = yyssp - yyss + 1;
+
+# if defined yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ yy_state_t *yyss1 = yyss;
+ YYSTYPE *yyvs1 = yyvs;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * YYSIZEOF (*yyssp),
+ &yyvs1, yysize * YYSIZEOF (*yyvsp),
+ &yystacksize);
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+# else /* defined YYSTACK_RELOCATE */
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yy_state_t *yyss1 = yyss;
+ union yyalloc *yyptr =
+ YY_CAST (union yyalloc *,
+ YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YY_IGNORE_USELESS_CAST_BEGIN
+ YYDPRINTF ((stderr, "Stack size increased to %ld\n",
+ YY_CAST (long, yystacksize)));
+ YY_IGNORE_USELESS_CAST_END
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = yylex (&yylval, p);
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+ yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ '$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 1566 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lstate = EXPR_BEG;
+ if (!p->locals) p->locals = cons(0,0);
+ }
+#line 6088 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 3:
+#line 1571 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->tree = new_scope(p, (yyvsp[0].nd));
+ NODE_LINENO(p->tree, (yyvsp[0].nd));
+ }
+#line 6097 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 4:
+#line 1578 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 6105 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 5:
+#line 1584 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_begin(p, 0);
+ }
+#line 6113 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 6:
+#line 1588 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_begin(p, (yyvsp[0].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[0].nd));
+ }
+#line 6122 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 7:
+#line 1593 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), newline_node((yyvsp[0].nd)));
+ }
+#line 6130 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 8:
+#line 1597 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_begin(p, 0);
+ }
+#line 6138 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 10:
+#line 1604 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = local_switch(p);
+ nvars_block(p);
+ }
+#line 6147 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 11:
+#line 1609 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "BEGIN not supported");
+ local_resume(p, (yyvsp[-3].nd));
+ nvars_unnest(p);
+ (yyval.nd) = 0;
+ }
+#line 6158 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 12:
+#line 1621 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if ((yyvsp[-2].nd)) {
+ (yyval.nd) = new_rescue(p, (yyvsp[-3].nd), (yyvsp[-2].nd), (yyvsp[-1].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[-3].nd));
+ }
+ else if ((yyvsp[-1].nd)) {
+ yywarn(p, "else without rescue is useless");
+ (yyval.nd) = push((yyvsp[-3].nd), (yyvsp[-1].nd));
+ }
+ else {
+ (yyval.nd) = (yyvsp[-3].nd);
+ }
+ if ((yyvsp[0].nd)) {
+ if ((yyval.nd)) {
+ (yyval.nd) = new_ensure(p, (yyval.nd), (yyvsp[0].nd));
+ }
+ else {
+ (yyval.nd) = push((yyvsp[0].nd), new_nil(p));
+ }
+ }
+ }
+#line 6184 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 13:
+#line 1645 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 6192 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 14:
+#line 1651 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_begin(p, 0);
+ }
+#line 6200 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 15:
+#line 1655 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_begin(p, (yyvsp[0].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[0].nd));
+ }
+#line 6209 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 16:
+#line 1660 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), newline_node((yyvsp[0].nd)));
+ }
+#line 6217 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 17:
+#line 1664 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_begin(p, (yyvsp[0].nd));
+ }
+#line 6225 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 18:
+#line 1669 "mrbgems/mruby-compiler/core/parse.y"
+ {p->lstate = EXPR_FNAME;}
+#line 6231 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 19:
+#line 1670 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_alias(p, (yyvsp[-2].id), (yyvsp[0].id));
+ }
+#line 6239 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 20:
+#line 1674 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 6247 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 21:
+#line 1678 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_if(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd), 0);
+ }
+#line 6255 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 22:
+#line 1682 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_unless(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd), 0);
+ }
+#line 6263 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 23:
+#line 1686 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_while(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd));
+ }
+#line 6271 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 24:
+#line 1690 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_until(p, cond((yyvsp[0].nd)), (yyvsp[-2].nd));
+ }
+#line 6279 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 25:
+#line 1694 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6287 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 26:
+#line 1698 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "END not supported");
+ (yyval.nd) = new_postexe(p, (yyvsp[-1].nd));
+ }
+#line 6296 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 28:
+#line 1704 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_masgn(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6304 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 29:
+#line 1708 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_asgn(p, (yyvsp[-2].nd), new_array(p, (yyvsp[0].nd)));
+ }
+#line 6312 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 30:
+#line 1712 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_masgn(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6320 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 31:
+#line 1716 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_masgn(p, (yyvsp[-2].nd), new_array(p, (yyvsp[0].nd)));
+ }
+#line 6328 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 32:
+#line 1720 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ node *lhs = new_lvar(p, (yyvsp[0].id));
+ void_expr_error(p, (yyvsp[-2].nd));
+ assignable(p, lhs);
+ (yyval.nd) = new_asgn(p, lhs, (yyvsp[-2].nd));
+ }
+#line 6339 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 34:
+#line 1730 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_asgn(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6347 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 35:
+#line 1734 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, (yyvsp[-2].nd), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 6355 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 36:
+#line 1738 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-5].nd), intern_op(aref), (yyvsp[-3].nd), '.'), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 6363 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 37:
+#line 1742 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 6371 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 38:
+#line 1746 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 6379 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 39:
+#line 1750 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "constant re-assignment");
+ (yyval.nd) = 0;
+ }
+#line 6388 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 40:
+#line 1755 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, tCOLON2), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 6396 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 41:
+#line 1759 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-3].nd);
+ endless_method_name(p, (yyvsp[-3].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ defn_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd));
+ nvars_unnest(p);
+ p->in_def--;
+ }
+#line 6409 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 42:
+#line 1768 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-5].nd);
+ endless_method_name(p, (yyvsp[-5].nd));
+ void_expr_error(p, (yyvsp[-2].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ defn_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd)));
+ nvars_unnest(p);
+ p->in_def--;
+ }
+#line 6423 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 43:
+#line 1778 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-3].nd);
+ void_expr_error(p, (yyvsp[0].nd));
+ defs_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd));
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
+#line 6436 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 44:
+#line 1787 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-5].nd);
+ void_expr_error(p, (yyvsp[-2].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ defs_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd)));
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
+#line 6450 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 45:
+#line 1797 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ backref_error(p, (yyvsp[-2].nd));
+ (yyval.nd) = new_begin(p, 0);
+ }
+#line 6459 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 47:
+#line 1805 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6467 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 50:
+#line 1814 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_and(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6475 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 51:
+#line 1818 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_or(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6483 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 52:
+#line 1822 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "!");
+ }
+#line 6491 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 53:
+#line 1826 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "!");
+ }
+#line 6499 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 55:
+#line 1834 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_def(p, (yyvsp[0].id), nint(p->cmdarg_stack), local_switch(p));
+ p->cmdarg_stack = 0;
+ p->in_def++;
+ nvars_block(p);
+ }
+#line 6510 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 56:
+#line 1843 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lstate = EXPR_FNAME;
+ }
+#line 6518 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 57:
+#line 1847 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_sdef(p, (yyvsp[-3].nd), (yyvsp[0].id), nint(p->cmdarg_stack), local_switch(p));
+ p->cmdarg_stack = 0;
+ p->in_def++;
+ p->in_single++;
+ nvars_block(p);
+ p->lstate = EXPR_ENDFN; /* force for args */
+ }
+#line 6531 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 58:
+#line 1858 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (!(yyvsp[0].nd)) (yyval.nd) = new_nil(p);
+ else {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+ }
+#line 6542 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 62:
+#line 1872 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num));
+ }
+#line 6550 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 63:
+#line 1878 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_nest(p);
+ nvars_nest(p);
+ }
+#line 6559 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 64:
+#line 1885 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_block(p, (yyvsp[-2].nd), (yyvsp[-1].nd));
+ local_unnest(p);
+ nvars_unnest(p);
+ }
+#line 6569 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 65:
+#line 1893 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_fcall(p, (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 6577 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 66:
+#line 1897 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ args_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd));
+ (yyval.nd) = new_fcall(p, (yyvsp[-2].id), (yyvsp[-1].nd));
+ }
+#line 6586 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 67:
+#line 1902 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num));
+ }
+#line 6594 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 68:
+#line 1906 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ args_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd));
+ (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), (yyvsp[-3].num));
+ }
+#line 6603 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 69:
+#line 1911 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), tCOLON2);
+ }
+#line 6611 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 70:
+#line 1915 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ args_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd));
+ (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), tCOLON2);
+ }
+#line 6620 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 71:
+#line 1920 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_super(p, (yyvsp[0].nd));
+ }
+#line 6628 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 72:
+#line 1924 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_yield(p, (yyvsp[0].nd));
+ }
+#line 6636 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 73:
+#line 1928 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_return(p, ret_args(p, (yyvsp[0].nd)));
+ }
+#line 6644 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 74:
+#line 1932 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_break(p, ret_args(p, (yyvsp[0].nd)));
+ }
+#line 6652 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 75:
+#line 1936 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_next(p, ret_args(p, (yyvsp[0].nd)));
+ }
+#line 6660 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 76:
+#line 1942 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 6668 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 77:
+#line 1946 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 6676 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 79:
+#line 1953 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 6684 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 80:
+#line 1959 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 6692 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 81:
+#line 1963 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1(push((yyvsp[-1].nd),(yyvsp[0].nd)));
+ }
+#line 6700 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 82:
+#line 1967 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list2((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6708 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 83:
+#line 1971 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3((yyvsp[-4].nd), (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6716 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 84:
+#line 1975 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list2((yyvsp[-1].nd), new_nil(p));
+ }
+#line 6724 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 85:
+#line 1979 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3((yyvsp[-3].nd), new_nil(p), (yyvsp[0].nd));
+ }
+#line 6732 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 86:
+#line 1983 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list2(0, (yyvsp[0].nd));
+ }
+#line 6740 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 87:
+#line 1987 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3(0, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 6748 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 88:
+#line 1991 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list2(0, new_nil(p));
+ }
+#line 6756 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 89:
+#line 1995 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3(0, new_nil(p), (yyvsp[0].nd));
+ }
+#line 6764 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 91:
+#line 2002 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_masgn(p, (yyvsp[-1].nd), NULL);
+ }
+#line 6772 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 92:
+#line 2008 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[-1].nd));
+ }
+#line 6780 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 93:
+#line 2012 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[-1].nd));
+ }
+#line 6788 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 94:
+#line 2018 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 6796 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 95:
+#line 2022 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 6804 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 96:
+#line 2028 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ assignable(p, (yyvsp[0].nd));
+ }
+#line 6812 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 97:
+#line 2032 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), intern_op(aref), (yyvsp[-1].nd), '.');
+ }
+#line 6820 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 98:
+#line 2036 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num));
+ }
+#line 6828 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 99:
+#line 2040 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, tCOLON2);
+ }
+#line 6836 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 100:
+#line 2044 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num));
+ }
+#line 6844 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 101:
+#line 2048 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ (yyval.nd) = new_colon2(p, (yyvsp[-2].nd), (yyvsp[0].id));
+ }
+#line 6854 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 102:
+#line 2054 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ (yyval.nd) = new_colon3(p, (yyvsp[0].id));
+ }
+#line 6864 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 103:
+#line 2060 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ backref_error(p, (yyvsp[0].nd));
+ (yyval.nd) = 0;
+ }
+#line 6873 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 104:
+#line 2067 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ assignable(p, (yyvsp[0].nd));
+ }
+#line 6881 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 105:
+#line 2071 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), intern_op(aref), (yyvsp[-1].nd), '.');
+ }
+#line 6889 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 106:
+#line 2075 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num));
+ }
+#line 6897 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 107:
+#line 2079 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, tCOLON2);
+ }
+#line 6905 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 108:
+#line 2083 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, (yyvsp[-1].num));
+ }
+#line 6913 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 109:
+#line 2087 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ (yyval.nd) = new_colon2(p, (yyvsp[-2].nd), (yyvsp[0].id));
+ }
+#line 6923 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 110:
+#line 2093 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ (yyval.nd) = new_colon3(p, (yyvsp[0].id));
+ }
+#line 6933 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 111:
+#line 2099 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ backref_error(p, (yyvsp[0].nd));
+ (yyval.nd) = 0;
+ }
+#line 6942 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 112:
+#line 2104 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "can't assign to numbered parameter");
+ }
+#line 6950 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 113:
+#line 2110 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "class/module name must be CONSTANT");
+ }
+#line 6958 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 115:
+#line 2117 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(nint(1), nsym((yyvsp[0].id)));
+ }
+#line 6966 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 116:
+#line 2121 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(nint(0), nsym((yyvsp[0].id)));
+ }
+#line 6974 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 117:
+#line 2125 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[-2].nd));
+ (yyval.nd) = cons((yyvsp[-2].nd), nsym((yyvsp[0].id)));
+ }
+#line 6983 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 121:
+#line 2135 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lstate = EXPR_ENDFN;
+ (yyval.id) = (yyvsp[0].id);
+ }
+#line 6992 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 122:
+#line 2140 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lstate = EXPR_ENDFN;
+ (yyval.id) = (yyvsp[0].id);
+ }
+#line 7001 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 125:
+#line 2151 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_undef(p, (yyvsp[0].id));
+ }
+#line 7009 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 126:
+#line 2154 "mrbgems/mruby-compiler/core/parse.y"
+ {p->lstate = EXPR_FNAME;}
+#line 7015 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 127:
+#line 2155 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-3].nd), nsym((yyvsp[0].id)));
+ }
+#line 7023 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 128:
+#line 2160 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(or); }
+#line 7029 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 129:
+#line 2161 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(xor); }
+#line 7035 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 130:
+#line 2162 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(and); }
+#line 7041 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 131:
+#line 2163 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(cmp); }
+#line 7047 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 132:
+#line 2164 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(eq); }
+#line 7053 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 133:
+#line 2165 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(eqq); }
+#line 7059 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 134:
+#line 2166 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(match); }
+#line 7065 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 135:
+#line 2167 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(nmatch); }
+#line 7071 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 136:
+#line 2168 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(gt); }
+#line 7077 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 137:
+#line 2169 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(ge); }
+#line 7083 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 138:
+#line 2170 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(lt); }
+#line 7089 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 139:
+#line 2171 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(le); }
+#line 7095 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 140:
+#line 2172 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(neq); }
+#line 7101 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 141:
+#line 2173 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(lshift); }
+#line 7107 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 142:
+#line 2174 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(rshift); }
+#line 7113 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 143:
+#line 2175 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(add); }
+#line 7119 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 144:
+#line 2176 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(sub); }
+#line 7125 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 145:
+#line 2177 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(mul); }
+#line 7131 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 146:
+#line 2178 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(mul); }
+#line 7137 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 147:
+#line 2179 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(div); }
+#line 7143 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 148:
+#line 2180 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(mod); }
+#line 7149 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 149:
+#line 2181 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(pow); }
+#line 7155 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 150:
+#line 2182 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(pow); }
+#line 7161 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 151:
+#line 2183 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(not); }
+#line 7167 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 152:
+#line 2184 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(neg); }
+#line 7173 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 153:
+#line 2185 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(plus); }
+#line 7179 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 154:
+#line 2186 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(minus); }
+#line 7185 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 155:
+#line 2187 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(aref); }
+#line 7191 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 156:
+#line 2188 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(aset); }
+#line 7197 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 157:
+#line 2189 "mrbgems/mruby-compiler/core/parse.y"
+ { (yyval.id) = intern_op(tick); }
+#line 7203 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 198:
+#line 2207 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_asgn(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7211 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 199:
+#line 2211 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, (yyvsp[-2].nd), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 7219 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 200:
+#line 2215 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-5].nd), intern_op(aref), (yyvsp[-3].nd), '.'), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 7227 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 201:
+#line 2219 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 7235 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 202:
+#line 2223 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, (yyvsp[-3].num)), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 7243 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 203:
+#line 2227 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_op_asgn(p, new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), 0, tCOLON2), (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 7251 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 204:
+#line 2231 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "constant re-assignment");
+ (yyval.nd) = new_begin(p, 0);
+ }
+#line 7260 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 205:
+#line 2236 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "constant re-assignment");
+ (yyval.nd) = new_begin(p, 0);
+ }
+#line 7269 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 206:
+#line 2241 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ backref_error(p, (yyvsp[-2].nd));
+ (yyval.nd) = new_begin(p, 0);
+ }
+#line 7278 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 207:
+#line 2246 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dot2(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7286 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 208:
+#line 2250 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dot2(p, (yyvsp[-1].nd), new_nil(p));
+ }
+#line 7294 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 209:
+#line 2254 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dot2(p, new_nil(p), (yyvsp[0].nd));
+ }
+#line 7302 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 210:
+#line 2258 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dot3(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7310 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 211:
+#line 2262 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dot3(p, (yyvsp[-1].nd), new_nil(p));
+ }
+#line 7318 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 212:
+#line 2266 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dot3(p, new_nil(p), (yyvsp[0].nd));
+ }
+#line 7326 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 213:
+#line 2270 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "+", (yyvsp[0].nd));
+ }
+#line 7334 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 214:
+#line 2274 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "-", (yyvsp[0].nd));
+ }
+#line 7342 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 215:
+#line 2278 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "*", (yyvsp[0].nd));
+ }
+#line 7350 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 216:
+#line 2282 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "/", (yyvsp[0].nd));
+ }
+#line 7358 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 217:
+#line 2286 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "%", (yyvsp[0].nd));
+ }
+#line 7366 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 218:
+#line 2290 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "**", (yyvsp[0].nd));
+ }
+#line 7374 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 219:
+#line 2294 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, call_bin_op(p, (yyvsp[-2].nd), "**", (yyvsp[0].nd)), "-@");
+ }
+#line 7382 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 220:
+#line 2298 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, call_bin_op(p, (yyvsp[-2].nd), "**", (yyvsp[0].nd)), "-@");
+ }
+#line 7390 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 221:
+#line 2302 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, (yyvsp[0].nd), "+@");
+ }
+#line 7398 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 222:
+#line 2306 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, (yyvsp[0].nd), "-@");
+ }
+#line 7406 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 223:
+#line 2310 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "|", (yyvsp[0].nd));
+ }
+#line 7414 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 224:
+#line 2314 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "^", (yyvsp[0].nd));
+ }
+#line 7422 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 225:
+#line 2318 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "&", (yyvsp[0].nd));
+ }
+#line 7430 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 226:
+#line 2322 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<=>", (yyvsp[0].nd));
+ }
+#line 7438 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 227:
+#line 2326 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), ">", (yyvsp[0].nd));
+ }
+#line 7446 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 228:
+#line 2330 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), ">=", (yyvsp[0].nd));
+ }
+#line 7454 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 229:
+#line 2334 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<", (yyvsp[0].nd));
+ }
+#line 7462 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 230:
+#line 2338 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<=", (yyvsp[0].nd));
+ }
+#line 7470 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 231:
+#line 2342 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "==", (yyvsp[0].nd));
+ }
+#line 7478 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 232:
+#line 2346 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "===", (yyvsp[0].nd));
+ }
+#line 7486 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 233:
+#line 2350 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "!=", (yyvsp[0].nd));
+ }
+#line 7494 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 234:
+#line 2354 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "=~", (yyvsp[0].nd));
+ }
+#line 7502 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 235:
+#line 2358 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "!~", (yyvsp[0].nd));
+ }
+#line 7510 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 236:
+#line 2362 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "!");
+ }
+#line 7518 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 237:
+#line 2366 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, cond((yyvsp[0].nd)), "~");
+ }
+#line 7526 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 238:
+#line 2370 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), "<<", (yyvsp[0].nd));
+ }
+#line 7534 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 239:
+#line 2374 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_bin_op(p, (yyvsp[-2].nd), ">>", (yyvsp[0].nd));
+ }
+#line 7542 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 240:
+#line 2378 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_and(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7550 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 241:
+#line 2382 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_or(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7558 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 242:
+#line 2386 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_if(p, cond((yyvsp[-5].nd)), (yyvsp[-3].nd), (yyvsp[0].nd));
+ }
+#line 7566 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 243:
+#line 2390 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_if(p, cond((yyvsp[-5].nd)), (yyvsp[-3].nd), (yyvsp[0].nd));
+ }
+#line 7574 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 244:
+#line 2394 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-3].nd);
+ endless_method_name(p, (yyvsp[-3].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ defn_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd));
+ nvars_unnest(p);
+ p->in_def--;
+ }
+#line 7587 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 245:
+#line 2403 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-5].nd);
+ endless_method_name(p, (yyvsp[-5].nd));
+ void_expr_error(p, (yyvsp[-2].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ defn_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd)));
+ nvars_unnest(p);
+ p->in_def--;
+ }
+#line 7601 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 246:
+#line 2413 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-3].nd);
+ void_expr_error(p, (yyvsp[0].nd));
+ defs_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[0].nd));
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
+#line 7614 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 247:
+#line 2422 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-5].nd);
+ void_expr_error(p, (yyvsp[-2].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ defs_setup(p, (yyval.nd), (yyvsp[-4].nd), new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd)));
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
+#line 7628 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 248:
+#line 2432 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 7636 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 250:
+#line 2439 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 7645 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 251:
+#line 2444 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-3].nd), new_kw_hash(p, (yyvsp[-1].nd)));
+ }
+#line 7653 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 252:
+#line 2448 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(new_kw_hash(p, (yyvsp[-1].nd)), 0);
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 7662 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 253:
+#line 2455 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 7670 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 254:
+#line 2459 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[-2].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = new_mod_rescue(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7680 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 255:
+#line 2467 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 7688 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 256:
+#line 2471 "mrbgems/mruby-compiler/core/parse.y"
+ {
+#if 1
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ (yyval.nd) = cons(push((yyvsp[-3].nd), new_splat(p, new_lvar(p, r))),
+ new_block_arg(p, new_lvar(p, b)));
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ (yyval.nd) = cons(list2(push((yyvsp[-3].nd), new_splat(p, new_lvar(p, r))),
+ new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k))))),
+ new_block_arg(p, new_lvar(p, b)));
+#endif
+ }
+#line 7708 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 257:
+#line 2487 "mrbgems/mruby-compiler/core/parse.y"
+ {
+#if 1
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ if (local_var_p(p, r) && local_var_p(p, b)) {
+ (yyval.nd) = cons(list1(new_splat(p, new_lvar(p, r))),
+ new_block_arg(p, new_lvar(p, b)));
+ }
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ if (local_var_p(p, r) && local_var_p(p, k) && local_var_p(p, b)) {
+ (yyval.nd) = cons(list2(new_splat(p, new_lvar(p, r)),
+ new_kw_hash(p, list1(cons(new_kw_rest_args(p, 0), new_lvar(p, k))))),
+ new_block_arg(p, new_lvar(p, b)));
+ }
+#endif
+ else {
+ yyerror(p, "unexpected argument forwarding ...");
+ (yyval.nd) = 0;
+ }
+ }
+#line 7736 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 262:
+#line 2519 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons((yyvsp[-1].nd),0);
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 7745 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 263:
+#line 2524 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(push((yyvsp[-3].nd), new_kw_hash(p, (yyvsp[-1].nd))), 0);
+ NODE_LINENO((yyval.nd), (yyvsp[-3].nd));
+ }
+#line 7754 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 264:
+#line 2529 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(list1(new_kw_hash(p, (yyvsp[-1].nd))), 0);
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 7763 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 265:
+#line 2536 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons(list1((yyvsp[0].nd)), 0);
+ NODE_LINENO((yyval.nd), (yyvsp[0].nd));
+ }
+#line 7773 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 266:
+#line 2542 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons((yyvsp[-1].nd), (yyvsp[0].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 7782 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 267:
+#line 2547 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(list1(new_kw_hash(p, (yyvsp[-1].nd))), (yyvsp[0].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 7791 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 268:
+#line 2552 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(push((yyvsp[-3].nd), new_kw_hash(p, (yyvsp[-1].nd))), (yyvsp[0].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[-3].nd));
+ }
+#line 7800 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 269:
+#line 2557 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(0, (yyvsp[0].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[0].nd));
+ }
+#line 7809 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 270:
+#line 2563 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.stack) = p->cmdarg_stack;
+ CMDARG_PUSH(1);
+ }
+#line 7818 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 271:
+#line 2568 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->cmdarg_stack = (yyvsp[-1].stack);
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 7827 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 272:
+#line 2575 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_block_arg(p, (yyvsp[0].nd));
+ }
+#line 7835 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 273:
+#line 2581 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 7843 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 274:
+#line 2585 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = 0;
+ }
+#line 7851 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 277:
+#line 2595 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons((yyvsp[0].nd), 0);
+ NODE_LINENO((yyval.nd), (yyvsp[0].nd));
+ }
+#line 7861 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 278:
+#line 2601 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons(new_splat(p, (yyvsp[0].nd)), 0);
+ NODE_LINENO((yyval.nd), (yyvsp[0].nd));
+ }
+#line 7871 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 279:
+#line 2607 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7880 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 280:
+#line 2612 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = push((yyvsp[-3].nd), new_splat(p, (yyvsp[0].nd)));
+ }
+#line 7889 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 281:
+#line 2619 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 7898 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 282:
+#line 2624 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = push((yyvsp[-3].nd), new_splat(p, (yyvsp[0].nd)));
+ }
+#line 7907 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 283:
+#line 2629 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = list1(new_splat(p, (yyvsp[0].nd)));
+ }
+#line 7916 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 291:
+#line 2643 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_nvar(p, (yyvsp[0].num));
+ }
+#line 7924 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 292:
+#line 2647 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_fcall(p, (yyvsp[0].id), 0);
+ }
+#line 7932 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 293:
+#line 2651 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.stack) = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+#line 7941 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 294:
+#line 2657 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->cmdarg_stack = (yyvsp[-2].stack);
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 7950 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 295:
+#line 2662 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.stack) = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+#line 7959 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 296:
+#line 2666 "mrbgems/mruby-compiler/core/parse.y"
+ {p->lstate = EXPR_ENDARG;}
+#line 7965 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 297:
+#line 2667 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->cmdarg_stack = (yyvsp[-3].stack);
+ (yyval.nd) = (yyvsp[-2].nd);
+ }
+#line 7974 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 298:
+#line 2671 "mrbgems/mruby-compiler/core/parse.y"
+ {p->lstate = EXPR_ENDARG;}
+#line 7980 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 299:
+#line 2672 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_nil(p);
+ }
+#line 7988 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 300:
+#line 2676 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 7996 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 301:
+#line 2680 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_colon2(p, (yyvsp[-2].nd), (yyvsp[0].id));
+ }
+#line 8004 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 302:
+#line 2684 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_colon3(p, (yyvsp[0].id));
+ }
+#line 8012 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 303:
+#line 2688 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_array(p, (yyvsp[-1].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 8021 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 304:
+#line 2693 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_hash(p, (yyvsp[-1].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[-1].nd));
+ }
+#line 8030 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 305:
+#line 2698 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_return(p, 0);
+ }
+#line 8038 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 306:
+#line 2702 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_yield(p, (yyvsp[0].nd));
+ }
+#line 8046 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 307:
+#line 2706 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, cond((yyvsp[-1].nd)), "!");
+ }
+#line 8054 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 308:
+#line 2710 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = call_uni_op(p, new_nil(p), "!");
+ }
+#line 8062 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 309:
+#line 2714 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_fcall(p, (yyvsp[-1].id), cons(0, (yyvsp[0].nd)));
+ }
+#line 8070 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 311:
+#line 2719 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ call_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd));
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 8079 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 312:
+#line 2724 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_nest(p);
+ (yyval.num) = p->lpar_beg;
+ p->lpar_beg = ++p->paren_nest;
+ }
+#line 8089 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 313:
+#line 2730 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.stack) = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+#line 8098 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 314:
+#line 2735 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lpar_beg = (yyvsp[-3].num);
+ (yyval.nd) = new_lambda(p, (yyvsp[-2].nd), (yyvsp[0].nd));
+ local_unnest(p);
+ p->cmdarg_stack = (yyvsp[-1].stack);
+ CMDARG_LEXPOP();
+ }
+#line 8110 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 315:
+#line 2746 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_if(p, cond((yyvsp[-4].nd)), (yyvsp[-2].nd), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-5].num));
+ }
+#line 8119 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 316:
+#line 2754 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_unless(p, cond((yyvsp[-4].nd)), (yyvsp[-2].nd), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-5].num));
+ }
+#line 8128 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 317:
+#line 2758 "mrbgems/mruby-compiler/core/parse.y"
+ {COND_PUSH(1);}
+#line 8134 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 318:
+#line 2758 "mrbgems/mruby-compiler/core/parse.y"
+ {COND_POP();}
+#line 8140 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 319:
+#line 2761 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_while(p, cond((yyvsp[-4].nd)), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-6].num));
+ }
+#line 8149 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 320:
+#line 2765 "mrbgems/mruby-compiler/core/parse.y"
+ {COND_PUSH(1);}
+#line 8155 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 321:
+#line 2765 "mrbgems/mruby-compiler/core/parse.y"
+ {COND_POP();}
+#line 8161 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 322:
+#line 2768 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_until(p, cond((yyvsp[-4].nd)), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-6].num));
+ }
+#line 8170 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 323:
+#line 2775 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_case(p, (yyvsp[-3].nd), (yyvsp[-1].nd));
+ }
+#line 8178 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 324:
+#line 2779 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_case(p, 0, (yyvsp[-1].nd));
+ }
+#line 8186 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 325:
+#line 2783 "mrbgems/mruby-compiler/core/parse.y"
+ {COND_PUSH(1);}
+#line 8192 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 326:
+#line 2785 "mrbgems/mruby-compiler/core/parse.y"
+ {COND_POP();}
+#line 8198 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 327:
+#line 2788 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_for(p, (yyvsp[-7].nd), (yyvsp[-4].nd), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-8].num));
+ }
+#line 8207 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 328:
+#line 2794 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "class definition in method body");
+ (yyval.nd) = local_switch(p);
+ nvars_block(p);
+ }
+#line 8218 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 329:
+#line 2802 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_class(p, (yyvsp[-4].nd), (yyvsp[-3].nd), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-5].num));
+ local_resume(p, (yyvsp[-2].nd));
+ nvars_unnest(p);
+ }
+#line 8229 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 330:
+#line 2810 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.num) = p->in_def;
+ p->in_def = 0;
+ }
+#line 8238 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 331:
+#line 2815 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(local_switch(p), nint(p->in_single));
+ nvars_block(p);
+ p->in_single = 0;
+ }
+#line 8248 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 332:
+#line 2822 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_sclass(p, (yyvsp[-5].nd), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-7].num));
+ local_resume(p, (yyvsp[-2].nd)->car);
+ nvars_unnest(p);
+ p->in_def = (yyvsp[-4].num);
+ p->in_single = intn((yyvsp[-2].nd)->cdr);
+ }
+#line 8261 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 333:
+#line 2832 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "module definition in method body");
+ (yyval.nd) = local_switch(p);
+ nvars_block(p);
+ }
+#line 8272 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 334:
+#line 2840 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_module(p, (yyvsp[-3].nd), (yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-4].num));
+ local_resume(p, (yyvsp[-2].nd));
+ nvars_unnest(p);
+ }
+#line 8283 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 335:
+#line 2850 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-3].nd);
+ defn_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[-1].nd));
+ nvars_unnest(p);
+ p->in_def--;
+ }
+#line 8294 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 336:
+#line 2860 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-3].nd);
+ defs_setup(p, (yyval.nd), (yyvsp[-2].nd), (yyvsp[-1].nd));
+ nvars_unnest(p);
+ p->in_def--;
+ p->in_single--;
+ }
+#line 8306 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 337:
+#line 2868 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_break(p, 0);
+ }
+#line 8314 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 338:
+#line 2872 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_next(p, 0);
+ }
+#line 8322 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 339:
+#line 2876 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_redo(p);
+ }
+#line 8330 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 340:
+#line 2880 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_retry(p);
+ }
+#line 8338 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 341:
+#line 2886 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ if (!(yyval.nd)) (yyval.nd) = new_nil(p);
+ }
+#line 8347 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 348:
+#line 2905 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_if(p, cond((yyvsp[-3].nd)), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8355 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 350:
+#line 2912 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 8363 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 351:
+#line 2918 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1(list1((yyvsp[0].nd)));
+ }
+#line 8371 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 353:
+#line 2925 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3((yyvsp[0].nd),0,0);
+ }
+#line 8379 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 354:
+#line 2929 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3((yyvsp[-3].nd), new_arg(p, (yyvsp[0].id)), 0);
+ }
+#line 8387 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 355:
+#line 2933 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3((yyvsp[-5].nd), new_arg(p, (yyvsp[-2].id)), (yyvsp[0].nd));
+ }
+#line 8395 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 356:
+#line 2937 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, 0);
+ (yyval.nd) = list3((yyvsp[-2].nd), nint(-1), 0);
+ }
+#line 8404 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 357:
+#line 2942 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3((yyvsp[-4].nd), nint(-1), (yyvsp[0].nd));
+ }
+#line 8412 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 358:
+#line 2946 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3(0, new_arg(p, (yyvsp[0].id)), 0);
+ }
+#line 8420 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 359:
+#line 2950 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3(0, new_arg(p, (yyvsp[-2].id)), (yyvsp[0].nd));
+ }
+#line 8428 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 360:
+#line 2954 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, 0);
+ (yyval.nd) = list3(0, nint(-1), 0);
+ }
+#line 8437 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 361:
+#line 2959 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, 0);
+ }
+#line 8445 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 362:
+#line 2963 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list3(0, nint(-1), (yyvsp[0].nd));
+ }
+#line 8453 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 363:
+#line 2969 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, (yyvsp[-3].nd), (yyvsp[-1].nd), (yyvsp[0].id));
+ }
+#line 8461 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 364:
+#line 2973 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, (yyvsp[-1].nd), 0, (yyvsp[0].id));
+ }
+#line 8469 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 365:
+#line 2977 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, 0, (yyvsp[-1].nd), (yyvsp[0].id));
+ }
+#line 8477 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 366:
+#line 2981 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, 0, 0, (yyvsp[0].id));
+ }
+#line 8485 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 367:
+#line 2987 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 8493 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 368:
+#line 2991 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, 0, 0, 0);
+ }
+#line 8501 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 369:
+#line 2997 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 8509 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 370:
+#line 3001 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-7].nd), (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8517 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 371:
+#line 3005 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-3].nd), (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd));
+ }
+#line 8525 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 372:
+#line 3009 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8533 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 373:
+#line 3013 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-3].nd), 0, (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 8541 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 374:
+#line 3017 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-2].nd), 0, 0, 0, (yyvsp[0].nd));
+ }
+#line 8549 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 375:
+#line 3021 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-5].nd), 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8557 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 376:
+#line 3025 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-1].nd), 0, 0, 0, (yyvsp[0].nd));
+ }
+#line 8565 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 377:
+#line 3029 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 8573 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 378:
+#line 3033 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8581 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 379:
+#line 3037 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd));
+ }
+#line 8589 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 380:
+#line 3041 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8597 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 381:
+#line 3045 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, 0, (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 8605 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 382:
+#line 3049 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8613 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 383:
+#line 3053 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, 0, 0, 0, (yyvsp[0].nd));
+ }
+#line 8621 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 384:
+#line 3059 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_blk(p, 0);
+ (yyval.nd) = 0;
+ }
+#line 8630 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 385:
+#line 3064 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->cmd_start = TRUE;
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 8639 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 386:
+#line 3070 "mrbgems/mruby-compiler/core/parse.y"
+ {local_add_blk(p, 0);}
+#line 8645 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 387:
+#line 3071 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = 0;
+ }
+#line 8653 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 388:
+#line 3075 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_blk(p, 0);
+ (yyval.nd) = 0;
+ }
+#line 8662 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 389:
+#line 3080 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-2].nd);
+ }
+#line 8670 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 390:
+#line 3087 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = 0;
+ }
+#line 8678 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 391:
+#line 3091 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = 0;
+ }
+#line 8686 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 394:
+#line 3101 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, (yyvsp[0].id));
+ new_bv(p, (yyvsp[0].id));
+ }
+#line 8695 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 396:
+#line 3109 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-2].nd);
+ }
+#line 8703 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 397:
+#line 3113 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 8711 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 398:
+#line 3119 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 8719 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 399:
+#line 3123 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 8727 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 400:
+#line 3129 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_nest(p);
+ nvars_nest(p);
+ }
+#line 8736 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 401:
+#line 3136 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_block(p,(yyvsp[-2].nd),(yyvsp[-1].nd));
+ local_unnest(p);
+ nvars_unnest(p);
+ }
+#line 8746 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 402:
+#line 3144 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if (typen((yyvsp[-1].nd)->car) == NODE_YIELD) {
+ yyerror(p, "block given to yield");
+ }
+ else {
+ call_with_block(p, (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 8760 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 403:
+#line 3154 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num));
+ }
+#line 8768 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 404:
+#line 3158 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), (yyvsp[-3].num));
+ call_with_block(p, (yyval.nd), (yyvsp[0].nd));
+ }
+#line 8777 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 405:
+#line 3163 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-4].nd), (yyvsp[-2].id), (yyvsp[-1].nd), (yyvsp[-3].num));
+ call_with_block(p, (yyval.nd), (yyvsp[0].nd));
+ }
+#line 8786 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 406:
+#line 3170 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_fcall(p, (yyvsp[-1].id), (yyvsp[0].nd));
+ }
+#line 8794 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 407:
+#line 3174 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), (yyvsp[-2].num));
+ }
+#line 8802 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 408:
+#line 3178 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), (yyvsp[-1].id), (yyvsp[0].nd), tCOLON2);
+ }
+#line 8810 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 409:
+#line 3182 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), (yyvsp[0].id), 0, tCOLON2);
+ }
+#line 8818 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 410:
+#line 3186 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), MRB_SYM_2(p->mrb, call), (yyvsp[0].nd), (yyvsp[-1].num));
+ }
+#line 8826 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 411:
+#line 3190 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-2].nd), MRB_SYM_2(p->mrb, call), (yyvsp[0].nd), tCOLON2);
+ }
+#line 8834 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 412:
+#line 3194 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_super(p, (yyvsp[0].nd));
+ }
+#line 8842 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 413:
+#line 3198 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_zsuper(p);
+ }
+#line 8850 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 414:
+#line 3202 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_call(p, (yyvsp[-3].nd), intern_op(aref), (yyvsp[-1].nd), '.');
+ }
+#line 8858 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 415:
+#line 3208 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_nest(p);
+ nvars_nest(p);
+ (yyval.num) = p->lineno;
+ }
+#line 8868 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 416:
+#line 3215 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_block(p,(yyvsp[-2].nd),(yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-3].num));
+ local_unnest(p);
+ nvars_unnest(p);
+ }
+#line 8879 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 417:
+#line 3222 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_nest(p);
+ nvars_nest(p);
+ (yyval.num) = p->lineno;
+ }
+#line 8889 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 418:
+#line 3229 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_block(p,(yyvsp[-2].nd),(yyvsp[-1].nd));
+ SET_LINENO((yyval.nd), (yyvsp[-3].num));
+ local_unnest(p);
+ nvars_unnest(p);
+ }
+#line 8900 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 419:
+#line 3240 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = cons(cons((yyvsp[-3].nd), (yyvsp[-1].nd)), (yyvsp[0].nd));
+ }
+#line 8908 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 420:
+#line 3246 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if ((yyvsp[0].nd)) {
+ (yyval.nd) = cons(cons(0, (yyvsp[0].nd)), 0);
+ }
+ else {
+ (yyval.nd) = 0;
+ }
+ }
+#line 8921 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 422:
+#line 3260 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1(list3((yyvsp[-4].nd), (yyvsp[-3].nd), (yyvsp[-1].nd)));
+ if ((yyvsp[0].nd)) (yyval.nd) = append((yyval.nd), (yyvsp[0].nd));
+ }
+#line 8930 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 424:
+#line 3268 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 8938 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 427:
+#line 3276 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 8946 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 429:
+#line 3283 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 8954 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 436:
+#line 3297 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = concat_string(p, (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8962 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 439:
+#line 3305 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 8970 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 440:
+#line 3309 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dstr(p, push((yyvsp[-1].nd), (yyvsp[0].nd)));
+ }
+#line 8978 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 442:
+#line 3316 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = append((yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 8986 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 443:
+#line 3322 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 8994 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 444:
+#line 3326 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = p->lex_strterm;
+ p->lex_strterm = NULL;
+ }
+#line 9003 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 445:
+#line 3332 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lex_strterm = (yyvsp[-2].nd);
+ (yyval.nd) = list2((yyvsp[-3].nd), (yyvsp[-1].nd));
+ }
+#line 9012 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 446:
+#line 3337 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1(new_literal_delim(p));
+ }
+#line 9020 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 447:
+#line 3341 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1(new_literal_delim(p));
+ }
+#line 9028 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 448:
+#line 3347 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 9036 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 449:
+#line 3351 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dxstr(p, push((yyvsp[-1].nd), (yyvsp[0].nd)));
+ }
+#line 9044 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 450:
+#line 3357 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 9052 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 451:
+#line 3361 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_dregx(p, (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 9060 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 455:
+#line 3374 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ parser_heredoc_info * inf = parsing_heredoc_inf(p);
+ inf->doc = push(inf->doc, new_str(p, "", 0));
+ heredoc_end(p);
+ }
+#line 9070 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 456:
+#line 3380 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ heredoc_end(p);
+ }
+#line 9078 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 459:
+#line 3390 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ parser_heredoc_info * inf = parsing_heredoc_inf(p);
+ inf->doc = push(inf->doc, (yyvsp[0].nd));
+ heredoc_treat_nextline(p);
+ }
+#line 9088 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 460:
+#line 3396 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = p->lex_strterm;
+ p->lex_strterm = NULL;
+ }
+#line 9097 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 461:
+#line 3402 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ parser_heredoc_info * inf = parsing_heredoc_inf(p);
+ p->lex_strterm = (yyvsp[-2].nd);
+ inf->doc = push(push(inf->doc, (yyvsp[-3].nd)), (yyvsp[-1].nd));
+ }
+#line 9107 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 462:
+#line 3410 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_words(p, list1((yyvsp[0].nd)));
+ }
+#line 9115 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 463:
+#line 3414 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_words(p, push((yyvsp[-1].nd), (yyvsp[0].nd)));
+ }
+#line 9123 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 464:
+#line 3421 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lstate = EXPR_ENDARG;
+ (yyval.nd) = new_sym(p, (yyvsp[0].id));
+ }
+#line 9132 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 465:
+#line 3426 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lstate = EXPR_ENDARG;
+ (yyval.nd) = new_dsym(p, new_dstr(p, push((yyvsp[-1].nd), (yyvsp[0].nd))));
+ }
+#line 9141 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 466:
+#line 3433 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.id) = (yyvsp[0].id);
+ }
+#line 9149 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 471:
+#line 3443 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.id) = new_strsym(p, (yyvsp[0].nd));
+ }
+#line 9157 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 472:
+#line 3447 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.id) = new_strsym(p, (yyvsp[0].nd));
+ }
+#line 9165 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 473:
+#line 3453 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_symbols(p, list1((yyvsp[0].nd)));
+ }
+#line 9173 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 474:
+#line 3457 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_symbols(p, push((yyvsp[-1].nd), (yyvsp[0].nd)));
+ }
+#line 9181 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 477:
+#line 3465 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = negate_lit(p, (yyvsp[0].nd));
+ }
+#line 9189 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 478:
+#line 3469 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = negate_lit(p, (yyvsp[0].nd));
+ }
+#line 9197 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 479:
+#line 3475 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_lvar(p, (yyvsp[0].id));
+ }
+#line 9205 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 480:
+#line 3479 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_ivar(p, (yyvsp[0].id));
+ }
+#line 9213 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 481:
+#line 3483 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_gvar(p, (yyvsp[0].id));
+ }
+#line 9221 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 482:
+#line 3487 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_cvar(p, (yyvsp[0].id));
+ }
+#line 9229 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 483:
+#line 3491 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_const(p, (yyvsp[0].id));
+ }
+#line 9237 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 484:
+#line 3497 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ assignable(p, (yyvsp[0].nd));
+ }
+#line 9245 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 485:
+#line 3501 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "can't assign to numbered parameter");
+ }
+#line 9253 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 486:
+#line 3507 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = var_reference(p, (yyvsp[0].nd));
+ }
+#line 9261 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 487:
+#line 3511 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_nil(p);
+ }
+#line 9269 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 488:
+#line 3515 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_self(p);
+ }
+#line 9277 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 489:
+#line 3519 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_true(p);
+ }
+#line 9285 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 490:
+#line 3523 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_false(p);
+ }
+#line 9293 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 491:
+#line 3527 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ const char *fn = mrb_sym_name_len(p->mrb, p->filename_sym, NULL);
+ if (!fn) {
+ fn = "(null)";
+ }
+ (yyval.nd) = new_str(p, fn, strlen(fn));
+ }
+#line 9305 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 492:
+#line 3535 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ char buf[16];
+
+ dump_int(p->lineno, buf);
+ (yyval.nd) = new_int(p, buf, 10, 0);
+ }
+#line 9316 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 493:
+#line 3542 "mrbgems/mruby-compiler/core/parse.y"
+ {
+#ifdef MRB_UTF8_STRING
+ const char *enc = "UTF-8";
+#else
+ const char *enc = "ASCII-8BIT";
+#endif
+ (yyval.nd) = new_str(p, enc, strlen(enc));
+ }
+#line 9329 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 496:
+#line 3557 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = 0;
+ }
+#line 9337 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 497:
+#line 3561 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lstate = EXPR_BEG;
+ p->cmd_start = TRUE;
+ }
+#line 9346 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 498:
+#line 3566 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 9354 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 501:
+#line 3582 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ p->lstate = EXPR_BEG;
+ p->cmd_start = TRUE;
+ }
+#line 9364 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 502:
+#line 3588 "mrbgems/mruby-compiler/core/parse.y"
+ {
+#if 1
+ /* til real keyword args implemented */
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r);
+ (yyval.nd) = new_args(p, (yyvsp[-3].nd), 0, r, 0,
+ new_args_tail(p, 0, 0, b));
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r); local_add_f(p, k);
+ (yyval.nd) = new_args(p, (yyvsp[-3].nd), 0, r, 0,
+ new_args_tail(p, 0, new_kw_rest_args(p, nsym(k)), b));
+#endif
+ }
+#line 9386 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 503:
+#line 3606 "mrbgems/mruby-compiler/core/parse.y"
+ {
+#if 1
+ /* til real keyword args implemented */
+ mrb_sym r = intern_op(mul);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r);
+ (yyval.nd) = new_args(p, 0, 0, r, 0,
+ new_args_tail(p, 0, 0, b));
+#else
+ mrb_sym r = intern_op(mul);
+ mrb_sym k = intern_op(pow);
+ mrb_sym b = intern_op(and);
+ local_add_f(p, r); local_add_f(p, k);
+ (yyval.nd) = new_args(p, 0, 0, r, 0,
+ new_args_tail(p, 0, new_kw_rest_args(p, nsym(k)), b));
+#endif
+ }
+#line 9408 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 505:
+#line 3627 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 9416 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 506:
+#line 3633 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_nest(p);
+ }
+#line 9424 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 507:
+#line 3639 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = new_kw_arg(p, (yyvsp[-1].id), cons((yyvsp[0].nd), locals_node(p)));
+ local_unnest(p);
+ }
+#line 9434 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 508:
+#line 3645 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_kw_arg(p, (yyvsp[0].id), 0);
+ local_unnest(p);
+ }
+#line 9443 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 509:
+#line 3652 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_kw_arg(p, (yyvsp[-1].id), cons((yyvsp[0].nd), locals_node(p)));
+ local_unnest(p);
+ }
+#line 9452 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 510:
+#line 3657 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_kw_arg(p, (yyvsp[0].id), 0);
+ local_unnest(p);
+ }
+#line 9461 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 511:
+#line 3664 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 9469 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 512:
+#line 3668 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 9477 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 513:
+#line 3674 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 9485 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 514:
+#line 3678 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 9493 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 517:
+#line 3688 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_kw_rest_args(p, nsym((yyvsp[0].id)));
+ }
+#line 9501 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 518:
+#line 3692 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_kw_rest_args(p, 0);
+ }
+#line 9509 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 519:
+#line 3698 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, (yyvsp[-3].nd), (yyvsp[-1].nd), (yyvsp[0].id));
+ }
+#line 9517 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 520:
+#line 3702 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, (yyvsp[-1].nd), 0, (yyvsp[0].id));
+ }
+#line 9525 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 521:
+#line 3706 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, 0, (yyvsp[-1].nd), (yyvsp[0].id));
+ }
+#line 9533 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 522:
+#line 3710 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, 0, 0, (yyvsp[0].id));
+ }
+#line 9541 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 523:
+#line 3716 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ }
+#line 9549 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 524:
+#line 3720 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args_tail(p, 0, 0, 0);
+ }
+#line 9557 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 525:
+#line 3726 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 9565 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 526:
+#line 3730 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-7].nd), (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 9573 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 527:
+#line 3734 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-3].nd), (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd));
+ }
+#line 9581 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 528:
+#line 3738 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-5].nd), (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 9589 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 529:
+#line 3742 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-3].nd), 0, (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 9597 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 530:
+#line 3746 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-5].nd), 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 9605 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 531:
+#line 3750 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, (yyvsp[-1].nd), 0, 0, 0, (yyvsp[0].nd));
+ }
+#line 9613 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 532:
+#line 3754 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 9621 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 533:
+#line 3758 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-5].nd), (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 9629 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 534:
+#line 3762 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-1].nd), 0, 0, (yyvsp[0].nd));
+ }
+#line 9637 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 535:
+#line 3766 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, (yyvsp[-3].nd), 0, (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 9645 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 536:
+#line 3770 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, 0, (yyvsp[-1].id), 0, (yyvsp[0].nd));
+ }
+#line 9653 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 537:
+#line 3774 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, 0, (yyvsp[-3].id), (yyvsp[-1].nd), (yyvsp[0].nd));
+ }
+#line 9661 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 538:
+#line 3778 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_args(p, 0, 0, 0, 0, (yyvsp[0].nd));
+ }
+#line 9669 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 539:
+#line 3782 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, intern_op(and));
+ (yyval.nd) = new_args(p, 0, 0, 0, 0, 0);
+ }
+#line 9678 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 540:
+#line 3789 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "formal argument cannot be a constant");
+ (yyval.nd) = 0;
+ }
+#line 9687 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 541:
+#line 3794 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "formal argument cannot be an instance variable");
+ (yyval.nd) = 0;
+ }
+#line 9696 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 542:
+#line 3799 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "formal argument cannot be a global variable");
+ (yyval.nd) = 0;
+ }
+#line 9705 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 543:
+#line 3804 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "formal argument cannot be a class variable");
+ (yyval.nd) = 0;
+ }
+#line 9714 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 544:
+#line 3809 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ yyerror(p, "formal argument cannot be a numbered parameter");
+ (yyval.nd) = 0;
+ }
+#line 9723 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 545:
+#line 3816 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.id) = 0;
+ }
+#line 9731 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 546:
+#line 3820 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, (yyvsp[0].id));
+ (yyval.id) = (yyvsp[0].id);
+ }
+#line 9740 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 547:
+#line 3827 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_arg(p, (yyvsp[0].id));
+ }
+#line 9748 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 548:
+#line 3831 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = local_switch(p);
+ }
+#line 9756 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 549:
+#line 3835 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = new_masgn_param(p, (yyvsp[-1].nd), p->locals->car);
+ local_resume(p, (yyvsp[-2].nd));
+ local_add_f(p, 0);
+ }
+#line 9766 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 550:
+#line 3843 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 9774 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 551:
+#line 3847 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 9782 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 552:
+#line 3853 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, (yyvsp[-1].id));
+ local_nest(p);
+ (yyval.id) = (yyvsp[-1].id);
+ }
+#line 9792 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 553:
+#line 3861 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons(nsym((yyvsp[-1].id)), cons((yyvsp[0].nd), locals_node(p)));
+ local_unnest(p);
+ }
+#line 9802 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 554:
+#line 3869 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons(nsym((yyvsp[-1].id)), cons((yyvsp[0].nd), locals_node(p)));
+ local_unnest(p);
+ }
+#line 9812 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 555:
+#line 3877 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 9820 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 556:
+#line 3881 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 9828 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 557:
+#line 3887 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ }
+#line 9836 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 558:
+#line 3891 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 9844 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 561:
+#line 3901 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, (yyvsp[0].id));
+ (yyval.id) = (yyvsp[0].id);
+ }
+#line 9853 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 562:
+#line 3906 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ local_add_f(p, intern_op(mul));
+ (yyval.id) = -1;
+ }
+#line 9862 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 565:
+#line 3917 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.id) = (yyvsp[0].id);
+ }
+#line 9870 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 566:
+#line 3923 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.id) = (yyvsp[0].id);
+ }
+#line 9878 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 567:
+#line 3927 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.id) = 0;
+ }
+#line 9886 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 568:
+#line 3933 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[0].nd);
+ if (!(yyval.nd)) (yyval.nd) = new_nil(p);
+ }
+#line 9895 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 569:
+#line 3937 "mrbgems/mruby-compiler/core/parse.y"
+ {p->lstate = EXPR_BEG;}
+#line 9901 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 570:
+#line 3938 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ if ((yyvsp[-1].nd) == 0) {
+ yyerror(p, "can't define singleton method for ().");
+ }
+ else {
+ switch (typen((yyvsp[-1].nd)->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;
+ }
+ }
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 9928 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 572:
+#line 3964 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = (yyvsp[-1].nd);
+ }
+#line 9936 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 573:
+#line 3970 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = list1((yyvsp[0].nd));
+ NODE_LINENO((yyval.nd), (yyvsp[0].nd));
+ }
+#line 9945 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 574:
+#line 3975 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = push((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 9953 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 577:
+#line 3985 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[-2].nd));
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons((yyvsp[-2].nd), (yyvsp[0].nd));
+ }
+#line 9963 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 578:
+#line 3991 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons(new_sym(p, (yyvsp[-2].id)), (yyvsp[0].nd));
+ }
+#line 9972 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 579:
+#line 3996 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ if (typen((yyvsp[-2].nd)->car) == NODE_DSTR) {
+ (yyval.nd) = cons(new_dsym(p, (yyvsp[-2].nd)), (yyvsp[0].nd));
+ }
+ else {
+ (yyval.nd) = cons(new_sym(p, new_strsym(p, (yyvsp[-2].nd))), (yyvsp[0].nd));
+ }
+ }
+#line 9986 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 580:
+#line 4006 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ void_expr_error(p, (yyvsp[0].nd));
+ (yyval.nd) = cons(new_kw_rest_args(p, 0), (yyvsp[0].nd));
+ }
+#line 9995 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 593:
+#line 4033 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.num) = '.';
+ }
+#line 10003 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 594:
+#line 4037 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.num) = 0;
+ }
+#line 10011 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 596:
+#line 4044 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.num) = tCOLON2;
+ }
+#line 10019 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 605:
+#line 4065 "mrbgems/mruby-compiler/core/parse.y"
+ {yyerrok;}
+#line 10025 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 608:
+#line 4071 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ p->lineno += (yyvsp[0].num);
+ p->column = 0;
+ }
+#line 10034 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+ case 611:
+#line 4082 "mrbgems/mruby-compiler/core/parse.y"
+ {
+ (yyval.nd) = 0;
+ }
+#line 10042 "mrbgems/mruby-compiler/core/y.tab.c"
+ break;
+
+
+#line 10046 "mrbgems/mruby-compiler/core/y.tab.c"
+
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now 'shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+ {
+ const int yylhs = yyr1[yyn] - YYNTOKENS;
+ const int yyi = yypgoto[yylhs] + *yyssp;
+ yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+ ? yytable[yyi]
+ : yydefgoto[yylhs]);
+ }
+
+ goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error. |
+`--------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (p, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc)));
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (p, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, p);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+ /* Pacify compilers when the user code never invokes YYERROR and the
+ label yyerrorlab therefore never appears in user code. */
+ if (0)
+ YYERROR;
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, p);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (p, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+
+/*-----------------------------------------------------.
+| yyreturn -- parsing is finished, return the result. |
+`-----------------------------------------------------*/
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, p);
+ }
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[+*yyssp], yyvsp, p);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+#line 4086 "mrbgems/mruby-compiler/core/parse.y"
+
+#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_NO_STDIO
+ if (p->filename_sym) {
+ const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: %s\n", 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_c(parser_state *p, const char *msg, char c)
+{
+ char buf[256];
+
+ strncpy(buf, msg, sizeof(buf) - 2);
+ buf[sizeof(buf) - 2] = '\0';
+ strncat(buf, &c, 1);
+ yyerror(p, buf);
+}
+
+static void
+yywarn(parser_state *p, const char *s)
+{
+ char* c;
+ size_t n;
+
+ if (! p->capture_errors) {
+#ifndef MRB_NO_STDIO
+ if (p->filename_sym) {
+ const char *filename = mrb_sym_name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: warning: %s\n", filename, p->lineno, p->column, s);
+ }
+ else {
+ fprintf(stderr, "line %d:%d: warning: %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 *msg, const char *s)
+{
+ char buf[256];
+
+ strncpy(buf, msg, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, s, sizeof(buf) - strlen(buf) - 1);
+ yywarning(p, buf);
+}
+
+static void
+backref_error(parser_state *p, node *n)
+{
+ int c;
+
+ c = intn(n->car);
+
+ if (c == NODE_NTH_REF) {
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr)+'0');
+ }
+ else if (c == NODE_BACK_REF) {
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr));
+ }
+ else {
+ mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %d", 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:
+ if (n->cdr) {
+ 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
+nextc0(parser_state *p)
+{
+ int c;
+
+ if (p->s && p->s < p->send) {
+ c = (unsigned char)*p->s++;
+ }
+ else {
+#ifndef MRB_NO_STDIO
+ if (p->f) {
+ c = fgetc(p->f);
+ if (feof(p->f)) return -1;
+ }
+ else
+#endif
+ return -1;
+ }
+ return c;
+}
+
+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 {
+ c = nextc0(p);
+ if (c < 0) goto eof;
+ }
+ if (c >= 0) {
+ p->column++;
+ }
+ if (c == '\r') {
+ const int lf = nextc0(p);
+ if (lf == '\n') {
+ return '\n';
+ }
+ if (lf > 0) pushback(p, lf);
+ }
+ 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(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_NO_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<limit; i++) {
+ buf[i] = nextc(p);
+ if (buf[i] < 0) goto eof;
+ if (!ISXDIGIT(buf[i])) {
+ pushback(p, buf[i]);
+ break;
+ }
+ }
+ }
+ else {
+ pushback(p, buf[0]);
+ }
+ hex = scan_hex(p, buf, i, &i);
+ if (i == 0 || hex > 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 void
+heredoc_count_indent(parser_heredoc_info *hinf, const char *str, size_t len, size_t spaces, size_t *offset)
+{
+ size_t indent = 0;
+ *offset = 0;
+ for (size_t i = 0; i < len; i++) {
+ size_t size;
+ if (str[i] == '\n')
+ break;
+ else if (str[i] == '\t')
+ size = 8;
+ else if (ISSPACE(str[i]))
+ size = 1;
+ else
+ break;
+ size_t nindent = indent + size;
+ if (nindent > spaces || nindent > hinf->indent)
+ break;
+ indent = nindent;
+ *offset += 1;
+ }
+}
+
+static void
+heredoc_remove_indent(parser_state *p, parser_heredoc_info *hinf)
+{
+ if (!hinf->remove_indent || hinf->indent == 0)
+ return;
+ node *indented, *n, *pair, *escaped, *nspaces;
+ const char *str;
+ size_t len, spaces, offset, start, end;
+ indented = hinf->indented;
+ while (indented) {
+ n = indented->car;
+ pair = n->car;
+ str = (char*)pair->car;
+ len = (size_t)pair->cdr;
+ escaped = n->cdr->car;
+ nspaces = n->cdr->cdr;
+ if (escaped) {
+ char *newstr = strndup(str, len);
+ size_t newlen = 0;
+ start = 0;
+ while (start < len) {
+ end = escaped ? (size_t)escaped->car : len;
+ if (end > len) end = len;
+ spaces = (size_t)nspaces->car;
+ size_t esclen = end - start;
+ heredoc_count_indent(hinf, str + start, esclen, spaces, &offset);
+ esclen -= offset;
+ memcpy(newstr + newlen, str + start + offset, esclen);
+ newlen += esclen;
+ start = end;
+ if (escaped)
+ escaped = escaped->cdr;
+ nspaces = nspaces->cdr;
+ }
+ if (newlen < len)
+ newstr[newlen] = '\0';
+ pair->car = (node*)newstr;
+ pair->cdr = (node*)newlen;
+ } else {
+ spaces = (size_t)nspaces->car;
+ heredoc_count_indent(hinf, str, len, spaces, &offset);
+ pair->car = (node*)(str + offset);
+ pair->cdr = (node*)(len - offset);
+ }
+ indented = indented->cdr;
+ }
+}
+
+static void
+heredoc_push_indented(parser_state *p, parser_heredoc_info *hinf, node *pair, node *escaped, node *nspaces, mrb_bool empty_line)
+{
+ hinf->indented = push(hinf->indented, cons(pair, cons(escaped, nspaces)));
+ while (nspaces) {
+ size_t tspaces = (size_t)nspaces->car;
+ if ((hinf->indent == ~0U || tspaces < hinf->indent) && !empty_line)
+ hinf->indent = tspaces;
+ nspaces = nspaces->cdr;
+ }
+}
+
+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;
+
+ mrb_bool unindent = hinf && hinf->remove_indent;
+ mrb_bool head = hinf && hinf->line_head;
+ mrb_bool empty = TRUE;
+ size_t spaces = 0;
+ size_t pos = -1;
+ node *escaped = NULL;
+ node *nspaces = NULL;
+
+ if (beg == 0) beg = -3; /* should never happen */
+ if (end == 0) end = -3;
+ newtok(p);
+ while ((c = nextc(p)) != end || nest_level != 0) {
+ pos++;
+ 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)) {
+ heredoc_remove_indent(p, hinf);
+ return tHEREDOC_END;
+ }
+ }
+ if (c < 0) {
+ char buf[256];
+ const char s1[] = "can't find heredoc delimiter \"";
+ const char s2[] = "\" anywhere before EOF";
+
+ if (sizeof(s1)+sizeof(s2)+strlen(hinf->term)+1 >= sizeof(buf)) {
+ yyerror(p, "can't find heredoc delimiter anywhere before EOF");
+ } else {
+ strcpy(buf, s1);
+ strcat(buf, hinf->term);
+ strcat(buf, s2);
+ yyerror(p, buf);
+ }
+ return 0;
+ }
+ node *nd = new_str(p, tok(p), toklen(p));
+ pylval.nd = nd;
+ if (unindent && head) {
+ nspaces = push(nspaces, nint(spaces));
+ heredoc_push_indented(p, hinf, nd->cdr, escaped, nspaces, empty && line_head);
+ }
+ return tHD_STRING_MID;
+ }
+ if (unindent && empty) {
+ if (c == '\t')
+ spaces += 8;
+ else if (ISSPACE(c))
+ ++spaces;
+ else
+ empty = FALSE;
+ }
+ 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 (unindent) {
+ nspaces = push(nspaces, nint(spaces));
+ escaped = push(escaped, nint(pos));
+ pos--;
+ empty = TRUE;
+ spaces = 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;
+ node *nd = new_str(p, tok(p), toklen(p));
+ pylval.nd = nd;
+ if (hinf) {
+ if (unindent && head) {
+ nspaces = push(nspaces, nint(spaces));
+ heredoc_push_indented(p, hinf, nd->cdr, escaped, nspaces, FALSE);
+ }
+ 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;
+ case 'o': break;
+ default: tokadd(p, re_opt); break;
+ }
+ }
+ pushback(p, re_opt);
+ if (toklen(p)) {
+ char msg[128];
+
+ strcpy(msg, "unknown regexp option");
+ tokfix(p);
+ if (toklen(p) > 1) {
+ strcat(msg, "s");
+ }
+ strcat(msg, " - ");
+ strncat(msg, tok(p), sizeof(msg) - strlen(msg) - 1);
+ 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
+number_literal_suffix(parser_state *p)
+{
+ int c, result = 0;
+ node *list = 0;
+ int column = p->column;
+ int mask = NUM_SUFFIX_R|NUM_SUFFIX_I;
+
+ while ((c = nextc(p)) != -1) {
+ list = push(list, nint(c));
+
+ if ((mask & NUM_SUFFIX_I) && c == 'i') {
+ result |= (mask & NUM_SUFFIX_I);
+ mask &= ~NUM_SUFFIX_I;
+ /* r after i, rational of complex is disallowed */
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if ((mask & NUM_SUFFIX_R) && c == 'r') {
+ result |= (mask & NUM_SUFFIX_R);
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if (!ISASCII(c) || ISALPHA(c) || c == '_') {
+ p->column = column;
+ if (p->pb) {
+ p->pb = append(list, p->pb);
+ }
+ else {
+ p->pb = list;
+ }
+ return 0;
+ }
+ pushback(p, c);
+ break;
+ }
+ return result;
+}
+
+static int
+heredoc_identifier(parser_state *p)
+{
+ int c;
+ int type = str_heredoc;
+ mrb_bool indent = FALSE;
+ mrb_bool squiggly = 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 == '-' || c == '~') {
+ if (c == '-')
+ indent = TRUE;
+ if (c == '~')
+ squiggly = 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, '-');
+ if (squiggly) 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 || squiggly;
+ info->remove_indent = squiggly;
+ info->indent = ~0U;
+ info->indented = NULL;
+ 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 nlines = 1;
+ 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);
+ p->column = 0;
+ switch (p->lstate) {
+ case EXPR_BEG:
+ case EXPR_FNAME:
+ case EXPR_DOT:
+ case EXPR_CLASS:
+ case EXPR_VALUE:
+ p->lineno++;
+ if (p->parsing_heredoc != NULL) {
+ if (p->lex_strterm) {
+ return parse_string(p);
+ }
+ }
+ goto retry;
+ default:
+ break;
+ }
+ if (p->parsing_heredoc != NULL) {
+ pylval.num = nlines;
+ return '\n';
+ }
+ while ((c = nextc(p))) {
+ switch (c) {
+ case ' ': case '\t': case '\f': case '\r':
+ case '\13': /* '\v' */
+ space_seen = 1;
+ break;
+ case '#': /* comment as a whitespace */
+ skip(p, '\n');
+ nlines++;
+ break;
+ case '.':
+ if (!peek(p, '.')) {
+ pushback(p, '.');
+ p->lineno+=nlines; nlines=1;
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ case '&':
+ if (peek(p, '.')) {
+ pushback(p, '&');
+ p->lineno+=nlines; nlines=1;
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ 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;
+ pylval.num = nlines;
+ return '\n';
+
+ case '*':
+ if ((c = nextc(p)) == '*') {
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern_op(pow);
+ 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_op(mul);
+ 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+=nlines; nlines=1;
+ 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_op(lshift);
+ 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_op(rshift);
+ 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];
+ char cc[] = { (char)c2, '\0' };
+
+ strcpy(buf, "invalid character syntax; use ?\\");
+ strncat(buf, cc, 2);
+ 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_op(andand);
+ 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_op(and);
+ 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_op(oror);
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ return tOROP;
+ }
+ if (c == '=') {
+ pylval.id = intern_op(or);
+ 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_op(add);
+ 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_op(sub);
+ 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 '.':
+ {
+ int is_beg = IS_BEG();
+ p->lstate = EXPR_BEG;
+ if ((c = nextc(p)) == '.') {
+ if ((c = nextc(p)) == '.') {
+ return is_beg ? tBDOT3 : tDOT3;
+ }
+ pushback(p, c);
+ return is_beg ? tBDOT2 : tDOT2;
+ }
+ pushback(p, c);
+ if (c >= 0 && ISDIGIT(c)) {
+ yyerror(p, "no .<digit> 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;
+ int suffix = 0;
+
+ 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;
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 16, suffix);
+ 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;
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 2, suffix);
+ 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;
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 10, suffix);
+ 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;
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 8, suffix);
+ 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);
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, "0", 10, suffix);
+ 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_c(p, "trailing non digit in number: ", (char)nondigit);
+ }
+ tokfix(p);
+ if (is_float) {
+#ifdef MRB_NO_FLOAT
+ yywarning_s(p, "floating-point numbers are not supported", tok(p));
+ pylval.nd = new_int(p, "0", 10, 0);
+ 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", tok(p));
+ }
+ else if (errno == ERANGE) {
+ yywarning_s(p, "float out of range", tok(p));
+ errno = 0;
+ }
+ suffix = number_literal_suffix(p);
+ if (seen_e && (suffix & NUM_SUFFIX_R)) {
+ pushback(p, 'r');
+ suffix &= ~NUM_SUFFIX_R;
+ }
+ pylval.nd = new_float(p, tok(p), suffix);
+ return tFLOAT;
+#endif
+ }
+ suffix = number_literal_suffix(p);
+ pylval.nd = new_int(p, tok(p), 10, suffix);
+ 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 (IS_END() || ISSPACE(c) || c == '#') {
+ pushback(p, c);
+ p->lstate = EXPR_BEG;
+ return ':';
+ }
+ pushback(p, c);
+ p->lstate = EXPR_FNAME;
+ return tSYMBEG;
+
+ case '/':
+ if (IS_BEG()) {
+ p->lex_strterm = new_strterm(p, str_regexp, '/', 0);
+ return tREGEXP_BEG;
+ }
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern_op(div);
+ 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_op(xor);
+ 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;
+ p->paren_nest--;
+ 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+=nlines; nlines=1;
+ 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_op(mod);
+ 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(tok(p), toklen(p));
+ return tGVAR;
+
+ case '-':
+ tokadd(p, '$');
+ tokadd(p, c);
+ c = nextc(p);
+ pushback(p, c);
+ gvar:
+ tokfix(p);
+ pylval.id = intern(tok(p), toklen(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(p, "capture group index must be <= " MRB_STRINGIZE(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_c(p, "wrong instance variable name: @", c);
+ }
+ else {
+ yyerror_c(p, "wrong class variable name: @@", c);
+ }
+ return 0;
+ }
+ if (!identchar(c)) {
+ pushback(p, c);
+ return '@';
+ }
+ break;
+
+ case '_':
+ token_column = newtok(p);
+ break;
+
+ default:
+ if (!identchar(c)) {
+ char buf[36];
+ const char s[] = "Invalid char in expression: 0x";
+ const char hexdigits[] = "0123456789ABCDEF";
+
+ strcpy(buf, s);
+ buf[sizeof(s)-1] = hexdigits[(c & 0xf0) >> 4];
+ buf[sizeof(s)] = hexdigits[(c & 0x0f)];
+ buf[sizeof(s)+1] = 0;
+ yyerror(p, buf);
+ 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;
+
+ case '_':
+ if (p->lstate != EXPR_FNAME && toklen(p) == 2 && ISDIGIT(tok(p)[1]) && p->nvars) {
+ int n = tok(p)[1] - '0';
+ int nvar;
+
+ if (n > 0) {
+ node *nvars = p->nvars->cdr;
+
+ while (nvars) {
+ nvar = intn(nvars->car);
+ if (nvar == -2) break; /* top of the scope */
+ if (nvar > 0) {
+ yywarning(p, "numbered parameter used in outer block");
+ break;
+ }
+ nvars->car = nint(-1);
+ nvars = nvars->cdr;
+ }
+ nvar = intn(p->nvars->car);
+ if (nvar == -1) {
+ yywarning(p, "numbered parameter used in inner block");
+ }
+ if (nvar >= -1) {
+ pylval.num = n;
+ p->lstate = EXPR_END;
+ return tNUMPARAM;
+ }
+ else {
+ yywarning(p, "identifier for numbered parameter; consider another name");
+ }
+ }
+ }
+ /* fall through */
+ 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 ((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(tok(p), toklen(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(tok(p), toklen(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; i<cxt->slen; i++) {
+ local_add_f(p, cxt->syms[i]);
+ }
+ }
+ p->capture_errors = cxt->capture_errors;
+ p->no_optimize = cxt->no_optimize;
+ p->upper = cxt->upper;
+ 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_NO_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
+mrbc_cleanup_local_variables(mrb_state *mrb, mrbc_context *c)
+{
+ if (c->syms) {
+ mrb_free(mrb, c->syms);
+ c->syms = NULL;
+ c->slen = 0;
+ }
+}
+
+MRB_API void
+mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
+{
+ mrb_sym sym;
+ uint16_t i;
+ mrb_sym* new_table;
+
+ sym = mrb_intern_cstr(p->mrb, f);
+ p->filename_sym = sym;
+ p->lineno = (p->filename_table_length > 0)? 0 : 1;
+
+ for (i = 0; i < p->filename_table_length; ++i) {
+ if (p->filename_table[i] == sym) {
+ p->current_filename_index = i;
+ return;
+ }
+ }
+
+ 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 mrb_sym
+mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
+ if (idx >= p->filename_table_length) return 0;
+ else {
+ return p->filename_table[idx];
+ }
+}
+
+#ifndef MRB_NO_STDIO
+static struct mrb_parser_state *
+mrb_parse_file_continue(mrb_state *mrb, FILE *f, const void *prebuf, size_t prebufsize, mrbc_context *c)
+{
+ parser_state *p;
+
+ p = mrb_parser_new(mrb);
+ if (!p) return NULL;
+ if (prebuf) {
+ p->s = (const char *)prebuf;
+ p->send = (const char *)prebuf + prebufsize;
+ }
+ else {
+ p->s = p->send = NULL;
+ }
+ p->f = f;
+
+ mrb_parser_parse(p, c);
+ return p;
+}
+
+MRB_API parser_state*
+mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
+{
+ return mrb_parse_file_continue(mrb, f, NULL, 0, c);
+}
+#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;
+ mrb_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];
+
+ strcpy(buf, "line ");
+ dump_int(p->error_buffer[0].lineno, buf+5);
+ strcat(buf, ": ");
+ strncat(buf, p->error_buffer[0].message, sizeof(buf) - strlen(buf) - 1);
+ mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, strlen(buf)));
+ mrb_parser_free(p);
+ return mrb_undef_value();
+ }
+ else {
+ if (mrb->exc == NULL) {
+ mrb->exc = mrb_obj_ptr(mrb_exc_new_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_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_vm_ci_target_class_set(mrb->c->ci, target);
+ }
+ v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep);
+ if (mrb->exc) return mrb_nil_value();
+ return v;
+}
+
+#ifndef MRB_NO_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);
+}
+
+#define DETECT_SIZE 64
+
+/*
+ * In order to be recognized as a `.mrb` file, the following three points must be satisfied:
+ * - File starts with "RITE"
+ * - At least `sizeof(struct rite_binary_header)` bytes can be read
+ * - `NUL` is included in the first 64 bytes of the file
+ */
+MRB_API mrb_value
+mrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, mrbc_context *c)
+{
+ union {
+ char b[DETECT_SIZE];
+ struct rite_binary_header h;
+ } leading;
+ size_t bufsize;
+
+ if (mrb == NULL || fp == NULL) {
+ return mrb_nil_value();
+ }
+
+ bufsize = fread(leading.b, sizeof(char), sizeof(leading), fp);
+ if (bufsize < sizeof(leading.h) ||
+ memcmp(leading.h.binary_ident, RITE_BINARY_IDENT, sizeof(leading.h.binary_ident)) != 0 ||
+ memchr(leading.b, '\0', bufsize) == NULL) {
+ return mrb_load_exec(mrb, mrb_parse_file_continue(mrb, fp, leading.b, bufsize, c), c);
+ }
+ else {
+ size_t binsize;
+ uint8_t *bin;
+ mrb_value bin_obj = mrb_nil_value(); /* temporary string object */
+ mrb_value result;
+
+ binsize = bin_to_uint32(leading.h.binary_size);
+ bin_obj = mrb_str_new(mrb, NULL, binsize);
+ bin = (uint8_t *)RSTRING_PTR(bin_obj);
+ memcpy(bin, leading.b, bufsize);
+ if (binsize > bufsize &&
+ fread(bin + bufsize, binsize - bufsize, 1, fp) == 0) {
+ binsize = bufsize;
+ /* The error is reported by mrb_load_irep_buf_cxt() */
+ }
+
+ result = mrb_load_irep_buf_cxt(mrb, bin, binsize, c);
+ if (mrb_string_p(bin_obj)) mrb_str_resize(mrb, bin_obj, 0);
+ return result;
+ }
+}
+#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_NO_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_sym_name(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_sym_name(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);
+ }
+}
+
+/*
+ * This function restores the GC arena on return.
+ * For this reason, if a process that further generates an object is
+ * performed at the caller, the string pointer returned as the return
+ * value may become invalid.
+ */
+static const char*
+str_dump(mrb_state *mrb, const char *str, int len)
+{
+ int ai = mrb_gc_arena_save(mrb);
+ mrb_value s;
+# if INT_MAX > MRB_INT_MAX / 4
+ /* check maximum length with "\xNN" character */
+ if (len > MRB_INT_MAX / 4) {
+ len = MRB_INT_MAX / 4;
+ }
+# endif
+ s = mrb_str_new(mrb, str, (mrb_int)len);
+ s = mrb_str_dump(mrb, s);
+ mrb_gc_arena_restore(mrb, ai);
+ return RSTRING_PTR(s);
+}
+#endif
+
+void
+mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
+{
+#ifndef MRB_NO_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_sym_name(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_sym_dump(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_sym_name(mrb, sym(tree->cdr)));
+ break;
+
+ case NODE_COLON3:
+ printf("NODE_COLON3: ::%s\n", mrb_sym_name(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 == nint(-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_sym_name(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_sym_name(mrb, sym(tree)));
+ break;
+
+ case NODE_GVAR:
+ printf("NODE_GVAR %s\n", mrb_sym_name(mrb, sym(tree)));
+ break;
+
+ case NODE_IVAR:
+ printf("NODE_IVAR %s\n", mrb_sym_name(mrb, sym(tree)));
+ break;
+
+ case NODE_CVAR:
+ printf("NODE_CVAR %s\n", mrb_sym_name(mrb, sym(tree)));
+ break;
+
+ case NODE_NVAR:
+ printf("NODE_NVAR %d\n", intn(tree));
+ break;
+
+ case NODE_CONST:
+ printf("NODE_CONST %s\n", mrb_sym_name(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_sym_name(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", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), 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", str_dump(mrb, (char*)tree->car, intn(tree->cdr)), 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_sym_dump(mrb, sym(tree)),
+ intn(tree));
+ break;
+
+ case NODE_DSYM:
+ printf("NODE_DSYM:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_WORDS:
+ printf("NODE_WORDS:\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_SYMBOLS:
+ printf("NODE_SYMBOLS:\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_LITERAL_DELIM:
+ printf("NODE_LITERAL_DELIM\n");
+ 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_sym_dump(mrb, sym(tree->car)),
+ mrb_sym_dump(mrb, sym(tree->cdr)));
+ break;
+
+ case NODE_UNDEF:
+ printf("NODE_UNDEF");
+ {
+ node *t = tree;
+ while (t) {
+ printf(" %s", mrb_sym_dump(mrb, sym(t->car)));
+ t = t->cdr;
+ }
+ }
+ printf(":\n");
+ break;
+
+ case NODE_CLASS:
+ printf("NODE_CLASS:\n");
+ if (tree->car->car == nint(0)) {
+ dump_prefix(tree, offset+1);
+ printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
+ }
+ else if (tree->car->car == nint(1)) {
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
+ }
+ else {
+ mrb_parser_dump(mrb, tree->car->car, offset+1);
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym_name(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 == nint(0)) {
+ dump_prefix(tree, offset+1);
+ printf(":%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
+ }
+ else if (tree->car->car == nint(1)) {
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym_name(mrb, sym(tree->car->cdr)));
+ }
+ else {
+ mrb_parser_dump(mrb, tree->car->car, offset+1);
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym_name(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_sym_dump(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_sym_name(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_sym_dump(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_sym_name(mrb, sym(tree->car)));
+ }
+ break;
+
+ case NODE_KW_ARG:
+ printf("NODE_KW_ARG %s:\n", mrb_sym_name(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_sym_name(mrb, sym(tree)));
+ break;
+
+ default:
+ printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
+ break;
+ }
+#endif
+}
+
+typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user);
+void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user);
+
+void
+mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user)
+{
+ const mrb_ast_node *n = p->tree;
+ if ((intptr_t)n->car == NODE_SCOPE) {
+ n = n->cdr->car;
+ for (; n; n = n->cdr) {
+ mrb_sym sym = (intptr_t)n->car;
+ if (sym && !func(mrb, sym, user)) break;
+ }
+ }
+}
diff --git a/mrbgems/mruby-compiler/mrbgem.rake b/mrbgems/mruby-compiler/mrbgem.rake
index e9e0cc2c7..3ecaa8908 100644
--- a/mrbgems/mruby-compiler/mrbgem.rake
+++ b/mrbgems/mruby-compiler/mrbgem.rake
@@ -3,38 +3,35 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
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"]
+ objs = %w[codegen y.tab].map do |name|
+ src = "#{dir}/core/#{name}.c"
+ if build.cxx_exception_enabled?
+ build.compile_as_cxx(src)
+ else
+ objfile(src.pathmap("#{build_dir}/core/%n"))
end
end
+ build.libmruby_core_objs << objs
+
+ lex_def = "#{dir}/core/lex.def"
# 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
+ file "#{dir}/core/y.tab.c" => ["#{dir}/core/parse.y", lex_def] do |t|
yacc.run t.name, t.prerequisites.first
+ replace_line_directive(t.name)
end
# Lexical analyzer
- file lex_def => "#{current_dir}/core/keywords" do |t|
+ file lex_def => "#{dir}/core/keywords" do |t|
gperf.run t.name, t.prerequisites.first
+ replace_line_directive(t.name)
end
- file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs
- build.libmruby << core_objs
+ def replace_line_directive(path)
+ content = File.read(path).gsub(%r{
+ ^\#line\s+\d+\s+"\K.*$ | # #line directive
+ ^/\*\s+Command-line:.*\s\K\S+(?=\s+\*/$) # header comment in lex.def
+ }x, &:relative_path)
+ File.write(path, content)
+ end
end
diff --git a/mrbgems/mruby-complex/mrbgem.rake b/mrbgems/mruby-complex/mrbgem.rake
new file mode 100644
index 000000000..cd81ecd02
--- /dev/null
+++ b/mrbgems/mruby-complex/mrbgem.rake
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-complex') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Complex class'
+ spec.build.defines << "MRB_USE_COMPLEX"
+ spec.add_dependency 'mruby-math', core: 'mruby-math'
+end
diff --git a/mrbgems/mruby-complex/mrblib/complex.rb b/mrbgems/mruby-complex/mrblib/complex.rb
new file mode 100644
index 000000000..4c36179cb
--- /dev/null
+++ b/mrbgems/mruby-complex/mrblib/complex.rb
@@ -0,0 +1,76 @@
+class Complex < Numeric
+ def self.polar(abs, arg = 0)
+ Complex(abs * Math.cos(arg), abs * Math.sin(arg))
+ end
+
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{real}#{'+' unless imaginary < 0}#{imaginary}#{'*' unless imaginary.finite?}i"
+ end
+
+ def +@
+ self
+ end
+
+ def -@
+ Complex(-real, -imaginary)
+ end
+
+ def abs
+ Math.hypot imaginary, real
+ end
+ alias_method :magnitude, :abs
+
+ def abs2
+ real * real + imaginary * imaginary
+ end
+
+ def arg
+ Math.atan2 imaginary, real
+ end
+ alias_method :angle, :arg
+ alias_method :phase, :arg
+
+ def conjugate
+ Complex(real, -imaginary)
+ end
+ alias_method :conj, :conjugate
+
+ def fdiv(numeric)
+ Complex(real / numeric, imaginary / numeric)
+ end
+
+ def polar
+ [abs, arg]
+ end
+
+ def real?
+ false
+ end
+
+ def rectangular
+ [real, imaginary]
+ end
+ alias_method :rect, :rectangular
+
+ def to_c
+ self
+ end
+
+ def to_r
+ raise RangeError.new "can't convert #{to_s} into Rational" unless imaginary.zero?
+ Rational(real, 1)
+ end
+
+ alias_method :imag, :imaginary
+
+ Numeric.class_eval do
+ def i
+ Complex(0, self)
+ end
+ end
+ undef i
+end
diff --git a/mrbgems/mruby-complex/src/complex.c b/mrbgems/mruby-complex/src/complex.c
new file mode 100644
index 000000000..286e7e38e
--- /dev/null
+++ b/mrbgems/mruby-complex/src/complex.c
@@ -0,0 +1,437 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/presym.h>
+#include <math.h>
+
+#ifdef MRB_NO_FLOAT
+# error Complex conflicts with 'MRB_NO_FLOAT' configuration
+#endif
+
+#ifdef MRB_USE_FLOAT32
+#define F(x) x##f
+#else
+#define F(x) x
+#endif
+
+struct mrb_complex {
+ mrb_float real;
+ mrb_float imaginary;
+};
+
+#if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32)
+
+struct RComplex {
+ MRB_OBJECT_HEADER;
+ struct mrb_complex *p;
+};
+
+static struct mrb_complex*
+complex_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct RComplex *r = (struct RComplex*)mrb_obj_ptr(v);
+
+ if (!r->p) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized complex");
+ }
+ return r->p;
+}
+
+#else
+#define COMPLEX_INLINE
+struct RComplex {
+ MRB_OBJECT_HEADER;
+ struct mrb_complex r;
+};
+#define complex_ptr(mrb, v) (&((struct RComplex*)mrb_obj_ptr(v))->r)
+#endif
+
+static struct RBasic*
+complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
+{
+ struct RComplex *s;
+ s = MRB_OBJ_ALLOC(mrb, MRB_TT_COMPLEX, c);
+#ifdef COMPLEX_INLINE
+ *p = &s->r;
+#else
+ *p = s->p = (struct mrb_complex*)mrb_malloc(mrb, sizeof(struct mrb_complex));
+#endif
+ return (struct RBasic*)s;
+}
+
+void
+mrb_complex_get(mrb_state *mrb, mrb_value cpx, mrb_float *r, mrb_float *i)
+{
+ struct mrb_complex *c = complex_ptr(mrb, cpx);
+
+ *r = c->real;
+ *i = c->imaginary;
+}
+
+mrb_value
+mrb_complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
+{
+ struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Complex));
+ struct mrb_complex *p;
+ struct RBasic *comp = complex_alloc(mrb, c, &p);
+ p->real = real;
+ p->imaginary = imaginary;
+ MRB_SET_FROZEN_FLAG(comp);
+
+ return mrb_obj_value(comp);
+}
+
+#define complex_new(mrb, real, imag) mrb_complex_new(mrb, real, imag)
+
+static mrb_value
+complex_real(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+ return mrb_float_value(mrb, p->real);
+}
+
+static mrb_value
+complex_imaginary(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+ return mrb_float_value(mrb, p->imaginary);
+}
+
+static mrb_value
+complex_s_rect(mrb_state *mrb, mrb_value self)
+{
+ mrb_float real, imaginary = 0.0;
+
+ mrb_get_args(mrb, "f|f", &real, &imaginary);
+ return complex_new(mrb, real, imaginary);
+}
+
+static mrb_value
+complex_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Float", self);
+ }
+
+ return mrb_float_value(mrb, p->real);
+}
+
+static mrb_value
+complex_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Float", self);
+ }
+ return mrb_int_value(mrb, (mrb_int)p->real);
+}
+
+static mrb_value
+complex_to_c(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+mrb_bool
+mrb_complex_eq(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ struct mrb_complex *p1 = complex_ptr(mrb, x);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_COMPLEX:
+ {
+ struct mrb_complex *p2 = complex_ptr(mrb, y);
+
+ if (p1->real == p2->real && p1->imaginary == p2->imaginary) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+ case MRB_TT_INTEGER:
+ if (p1->imaginary != 0) return FALSE;
+ return p1->real == mrb_integer(y);
+ case MRB_TT_FLOAT:
+ if (p1->imaginary != 0) return FALSE;
+ return p1->real == mrb_float(y);
+
+ default:
+ return mrb_equal(mrb, y, x);
+ }
+}
+
+static mrb_value
+complex_eq(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ return mrb_bool_value(mrb_complex_eq(mrb, x, y));
+}
+
+static mrb_value
+complex_add(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ struct mrb_complex *p1 = complex_ptr(mrb, x);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_COMPLEX:
+ {
+ struct mrb_complex *p2 = complex_ptr(mrb, y);
+ return mrb_complex_new(mrb, p1->real+p2->real, p1->imaginary+p2->imaginary);
+ }
+
+ default:
+ {
+ mrb_float z = mrb_as_float(mrb, y);
+ return mrb_complex_new(mrb, p1->real+z, p1->imaginary);
+ }
+ }
+}
+
+static mrb_value
+complex_sub(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ struct mrb_complex *p1 = complex_ptr(mrb, x);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_COMPLEX:
+ {
+ struct mrb_complex *p2 = complex_ptr(mrb, y);
+ return mrb_complex_new(mrb, p1->real-p2->real, p1->imaginary-p2->imaginary);
+ }
+
+ default:
+ {
+ mrb_float z = mrb_as_float(mrb, y);
+ return mrb_complex_new(mrb, p1->real-z, p1->imaginary);
+ }
+ }
+}
+
+static mrb_value
+complex_mul(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ struct mrb_complex *p1 = complex_ptr(mrb, x);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_COMPLEX:
+ {
+ struct mrb_complex *p2 = complex_ptr(mrb, y);
+ return mrb_complex_new(mrb, p1->real*p2->real - p1->imaginary*p2->imaginary,
+ p1->real*p2->imaginary + p2->real*p1->imaginary);
+ }
+
+ default:
+ {
+ mrb_float z = mrb_as_float(mrb, y);
+ return mrb_complex_new(mrb, p1->real*z, p1->imaginary*z);
+ }
+ }
+}
+
+/* Arithmetic on (significand, exponent) pairs avoids premature overflow in
+ complex division */
+struct float_pair {
+ mrb_float s;
+ int x;
+};
+
+static void
+add_pair(struct float_pair *s, struct float_pair const *a,
+ struct float_pair const *b)
+{
+ if (b->s == 0.0F) {
+ *s = *a;
+ } else if (a->s == 0.0F) {
+ *s = *b;
+ } else if (a->x >= b->x) {
+ s->s = a->s + F(ldexp)(b->s, b->x - a->x);
+ s->x = a->x;
+ } else {
+ s->s = F(ldexp)(a->s, a->x - b->x) + b->s;
+ s->x = b->x;
+ }
+}
+
+static void
+mul_pair(struct float_pair *p, struct float_pair const *a,
+ struct float_pair const *b)
+{
+ p->s = a->s * b->s;
+ p->x = a->x + b->x;
+}
+
+static void
+div_pair(struct float_pair *q, struct float_pair const *a,
+ struct float_pair const *b)
+{
+ q->s = mrb_div_float(a->s, b->s);
+ q->x = a->x - b->x;
+}
+
+static mrb_value
+complex_div(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *a, *b;
+ mrb_value rhs = mrb_get_arg1(mrb);
+
+ a = complex_ptr(mrb, self);
+ if (mrb_type(rhs) != MRB_TT_COMPLEX) {
+ mrb_float f = mrb_as_float(mrb, rhs);
+ return complex_new(mrb, mrb_div_float(a->real, f), mrb_div_float(a->imaginary, f));
+ }
+
+ struct float_pair ar, ai, br, bi;
+ struct float_pair br2, bi2;
+ struct float_pair div;
+ struct float_pair ar_br, ai_bi;
+ struct float_pair ai_br, ar_bi;
+ struct float_pair zr, zi;
+
+ b = complex_ptr(mrb, rhs);
+
+ /* Split floating-point components into significand and exponent */
+ ar.s = F(frexp)(a->real, &ar.x);
+ ai.s = F(frexp)(a->imaginary, &ai.x);
+ br.s = F(frexp)(b->real, &br.x);
+ bi.s = F(frexp)(b->imaginary, &bi.x);
+
+ /* Perform arithmetic on (significand, exponent) pairs to produce
+ the result: */
+
+ /* the divisor */
+ mul_pair(&br2, &br, &br);
+ mul_pair(&bi2, &bi, &bi);
+ add_pair(&div, &br2, &bi2);
+
+ /* real component */
+ mul_pair(&ar_br, &ar, &br);
+ mul_pair(&ai_bi, &ai, &bi);
+ add_pair(&zr, &ar_br, &ai_bi);
+ div_pair(&zr, &zr, &div);
+
+ /* imaginary component */
+ mul_pair(&ai_br, &ai, &br);
+ mul_pair(&ar_bi, &ar, &bi);
+ ar_bi.s = -ar_bi.s;
+ add_pair(&zi, &ai_br, &ar_bi);
+ div_pair(&zi, &zi, &div);
+
+ /* assemble the result */
+ return complex_new(mrb, F(ldexp)(zr.s, zr.x), F(ldexp)(zi.s, zi.x));
+}
+
+mrb_int mrb_div_int(mrb_state *mrb, mrb_int x, mrb_int y);
+mrb_value mrb_rational_new(mrb_state *mrb, mrb_int n, mrb_int d);
+mrb_value mrb_rational_div(mrb_state *mrb, mrb_value x);
+
+/* 15.2.8.3.4 */
+/*
+ * redefine Integer#/
+ */
+static mrb_value
+cpx_int_div(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a = mrb_integer(x);
+
+ if (mrb_integer_p(y)) {
+ mrb_int div = mrb_div_int(mrb, a, mrb_integer(y));
+ return mrb_int_value(mrb, div);
+ }
+ switch (mrb_type(y)) {
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ return mrb_rational_div(mrb, mrb_rational_new(mrb, a, 1));
+#endif
+ case MRB_TT_COMPLEX:
+ x = complex_new(mrb, (mrb_float)a, 0);
+ return complex_div(mrb, x);
+ default:
+ return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
+ }
+}
+
+/* 15.2.9.3.19(x) */
+/*
+ * redefine Integer#quo
+ */
+
+static mrb_value
+cpx_int_quo(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a = mrb_integer(x);
+
+ switch (mrb_type(y)) {
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ x = mrb_rational_new(mrb, a, 1);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(div), 1, y);
+#endif
+ case MRB_TT_COMPLEX:
+ x = complex_new(mrb, (mrb_float)a, 0);
+ return complex_div(mrb, x);
+ default:
+ return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
+ }
+}
+
+static mrb_value
+cpx_flo_div(mrb_state *mrb, mrb_value x)
+{
+ mrb_float a = mrb_float(x);
+ mrb_value y = mrb_get_arg1(mrb);
+
+ switch(mrb_type(y)) {
+ case MRB_TT_COMPLEX:
+ return complex_div(mrb, complex_new(mrb, a, 0));
+ case MRB_TT_FLOAT:
+ a = mrb_div_float(a, mrb_float(y));
+ return mrb_float_value(mrb, a);
+ default:
+ a = mrb_div_float(a, mrb_as_float(mrb, y));
+ return mrb_float_value(mrb, a);
+ }
+}
+
+void mrb_mruby_complex_gem_init(mrb_state *mrb)
+{
+ struct RClass *comp;
+
+#ifdef COMPLEX_INLINE
+ mrb_assert(sizeof(struct mrb_complex) < sizeof(void*)*3);
+#endif
+
+ comp = mrb_define_class_id(mrb, MRB_SYM(Complex), mrb_class_get_id(mrb, MRB_SYM(Numeric)));
+ MRB_SET_INSTANCE_TT(comp, MRB_TT_COMPLEX);
+
+ mrb_undef_class_method(mrb, comp, "new");
+ mrb_define_class_method(mrb, comp, "rectangular", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, comp, "rect", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, mrb->kernel_module, "Complex", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, comp, "real", complex_real, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "imaginary", complex_imaginary, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "to_f", complex_to_f, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "to_i", complex_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "to_c", complex_to_c, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "+", complex_add, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, comp, "-", complex_sub, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, comp, "*", complex_mul, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, comp, "/", complex_div, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, comp, "quo", complex_div, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, comp, "==", complex_eq, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mrb->integer_class, "/", cpx_int_div, MRB_ARGS_REQ(1)); /* override */
+ mrb_define_method(mrb, mrb->integer_class, "quo", cpx_int_quo, MRB_ARGS_REQ(1)); /* override */
+ mrb_define_method(mrb, mrb->float_class, "/", cpx_flo_div, MRB_ARGS_REQ(1)); /* override */
+ mrb_define_method(mrb, mrb->float_class, "quo", cpx_flo_div, MRB_ARGS_REQ(1)); /* override */
+}
+
+void
+mrb_mruby_complex_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-complex/test/complex.rb b/mrbgems/mruby-complex/test/complex.rb
new file mode 100644
index 000000000..85ee6e998
--- /dev/null
+++ b/mrbgems/mruby-complex/test/complex.rb
@@ -0,0 +1,163 @@
+def assert_complex(real, exp)
+ assert "assert_complex" do
+ assert_float real.real, exp.real
+ assert_float real.imaginary, exp.imaginary
+ end
+end
+
+assert 'Complex' do
+ c = 123i
+ assert_equal Complex, c.class
+ assert_equal [c.real, c.imaginary], [0, 123]
+ c = 123 + -1.23i
+ assert_equal Complex, c.class
+ assert_equal [c.real, c.imaginary], [123, -1.23]
+end
+
+assert 'Complex::polar' do
+ assert_complex Complex.polar(3, 0), (3 + 0i)
+ assert_complex Complex.polar(3, Math::PI/2), (0 + 3i)
+ assert_complex Complex.polar(3, Math::PI), (-3 + 0i)
+ assert_complex Complex.polar(3, -Math::PI/2), (0 + -3i)
+end
+
+assert 'Complex::rectangular' do
+ assert_complex Complex.rectangular(1, 2), (1 + 2i)
+end
+
+assert 'Complex#*' do
+ assert_complex Complex(2, 3) * Complex(2, 3), (-5 + 12i)
+ assert_complex Complex(900) * Complex(1), (900 + 0i)
+ assert_complex Complex(-2, 9) * Complex(-9, 2), (0 - 85i)
+ assert_complex Complex(9, 8) * 4, (36 + 32i)
+ assert_complex Complex(20, 9) * 9.8, (196.0 + 88.2i)
+ assert_complex 4 * Complex(9, 8), (36 + 32i)
+ assert_complex 9.8 * Complex(20, 9), (196.0 + 88.2i)
+end
+
+assert 'Complex#+' do
+ assert_complex Complex(2, 3) + Complex(2, 3) , (4 + 6i)
+ assert_complex Complex(900) + Complex(1) , (901 + 0i)
+ assert_complex Complex(-2, 9) + Complex(-9, 2), (-11 + 11i)
+ assert_complex Complex(9, 8) + 4 , (13 + 8i)
+ assert_complex Complex(20, 9) + 9.8 , (29.8 + 9i)
+ assert_complex 4 + Complex(9, 8) , (13 + 8i)
+ assert_complex 9.8 + Complex(20, 9) , (29.8 + 9i)
+end
+
+assert 'Complex#-' do
+ assert_complex Complex(2, 3) - Complex(2, 3) , (0 + 0i)
+ assert_complex Complex(900) - Complex(1) , (899 + 0i)
+ assert_complex Complex(-2, 9) - Complex(-9, 2), (7 + 7i)
+ assert_complex Complex(9, 8) - 4 , (5 + 8i)
+ assert_complex Complex(20, 9) - 9.8 , (10.2 + 9i)
+ assert_complex 4 - Complex(9, 8) , (-5 - 8i)
+ assert_complex 10.5 - Complex(20, 9) , (-9.5 - 9i)
+end
+
+assert 'Complex#-@' do
+ assert_complex((-1 - 2i), -Complex(1, 2))
+end
+
+assert 'Complex#/' do
+ assert_complex Complex(2, 3) / Complex(2, 3) , (1 + 0i)
+ assert_complex Complex(900) / Complex(1) , (900 + 0i)
+ assert_complex Complex(-2, 9) / Complex(-9, 2), ((36.0 / 85) - (77i / 85))
+ assert_complex Complex(9, 8) / 4 , ((9.0 / 4) + 2i)
+ assert_complex Complex(20, 9) / 9.8 , (2.0408163265306123 + 0.9183673469387754i)
+ assert_complex 4 / Complex(9, 8) , (0.2482758620689655 - 0.2206896551724138i)
+ assert_complex 9.8 / Complex(20, 9) , (0.4074844074844075 - 0.1833679833679834i)
+ if 1e39.infinite? then
+ # MRB_USE_FLOAT32 in effect
+ ten = 1e21
+ one = 1e20
+ else
+ ten = 1e201
+ one = 1e200
+ end
+ assert_complex Complex(ten, ten) / Complex(one, one), Complex(10.0, 0.0)
+end
+
+assert 'Complex#==' do
+ assert_true Complex(2, 3) == Complex(2, 3)
+ assert_true Complex(5) == 5
+ assert_true Complex(0) == 0.0
+ assert_true 5 == Complex(5)
+ assert_true 0.0 == Complex(0)
+end
+
+assert 'Complex#abs' do
+ assert_float Complex(-1).abs, 1
+ assert_float Complex(3.0, -4.0).abs, 5.0
+ if 1e39.infinite? then
+ # MRB_USE_FLOAT32 in effect
+ exp = 125
+ else
+ exp = 1021
+ end
+ assert_true Complex(3.0*2.0**exp, 4.0*2.0**exp).abs.finite?
+ assert_float Complex(3.0*2.0**exp, 4.0*2.0**exp).abs, 5.0*2.0**exp
+end
+
+assert 'Complex#abs2' do
+ assert_float Complex(-1).abs2, 1
+ assert_float Complex(3.0, -4.0).abs2, 25.0
+end
+
+assert 'Complex#arg' do
+ assert_float Complex.polar(3, Math::PI/2).arg, 1.5707963267948966
+end
+
+assert 'Complex#conjugate' do
+ assert_complex Complex(1, 2).conjugate, (1 - 2i)
+end
+
+assert 'Complex#fdiv' do
+ assert_complex Complex(11, 22).fdiv(3), (3.6666666666666665 + 7.333333333333333i)
+end
+
+assert 'Complex#imaginary' do
+ assert_float Complex(7).imaginary , 0
+ assert_float Complex(9, -4).imaginary, -4
+end
+
+assert 'Complex#polar' do
+ assert_equal Complex(1, 2).polar, [2.23606797749979, 1.1071487177940904]
+end
+
+assert 'Complex#real' do
+ assert_float Complex(7).real, 7
+ assert_float Complex(9, -4).real, 9
+end
+
+assert 'Complex#real?' do
+ assert_false Complex(1).real?
+end
+
+assert 'Complex::rectangular' do
+ assert_equal Complex(1, 2).rectangular, [1, 2]
+end
+
+assert 'Complex::to_c' do
+ assert_equal Complex(1, 2).to_c, Complex(1, 2)
+end
+
+assert 'Complex::to_f' do
+ assert_float Complex(1, 0).to_f, 1.0
+ assert_raise(RangeError) do
+ Complex(1, 2).to_f
+ end
+end
+
+assert 'Complex::to_i' do
+ assert_equal Complex(1, 0).to_i, 1
+ assert_raise(RangeError) do
+ Complex(1, 2).to_i
+ end
+end
+
+assert 'Complex#frozen?' do
+ assert_predicate(1i, :frozen?)
+ assert_predicate(Complex(2,3), :frozen?)
+ assert_predicate(4+5i, :frozen?)
+end
diff --git a/mrbgems/mruby-enum-chain/mrbgem.rake b/mrbgems/mruby-enum-chain/mrbgem.rake
new file mode 100644
index 000000000..7294f2644
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/mrbgem.rake
@@ -0,0 +1,6 @@
+MRuby::Gem::Specification.new('mruby-enum-chain') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Enumerator::Chain class'
+ spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
+end
diff --git a/mrbgems/mruby-enum-chain/mrblib/chain.rb b/mrbgems/mruby-enum-chain/mrblib/chain.rb
new file mode 100644
index 000000000..43d0926c8
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/mrblib/chain.rb
@@ -0,0 +1,62 @@
+##
+# chain.rb Enumerator::Chain class
+# See Copyright Notice in mruby.h
+
+module Enumerable
+ def chain(*args)
+ Enumerator::Chain.new(self, *args)
+ end
+end
+
+class Enumerator
+ def +(other)
+ Chain.new(self, other)
+ end
+
+ class Chain
+ include Enumerable
+
+ def initialize(*args)
+ @enums = args.freeze
+ @pos = -1
+ end
+
+ def each(&block)
+ return to_enum unless block
+
+ i = 0
+ while i < @enums.size
+ @pos = i
+ @enums[i].each(&block)
+ i += 1
+ end
+
+ self
+ end
+
+ def size
+ @enums.reduce(0) do |a, e|
+ return nil unless e.respond_to?(:size)
+ a + e.size
+ end
+ end
+
+ def rewind
+ while 0 <= @pos && @pos < @enums.size
+ e = @enums[@pos]
+ e.rewind if e.respond_to?(:rewind)
+ @pos -= 1
+ end
+
+ self
+ end
+
+ def +(other)
+ self.class.new(self, other)
+ end
+
+ def inspect
+ "#<#{self.class}: #{@enums.inspect}>"
+ end
+ end
+end
diff --git a/mrbgems/mruby-enum-chain/test/enum_chain.rb b/mrbgems/mruby-enum-chain/test/enum_chain.rb
new file mode 100644
index 000000000..45bbc9a77
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/test/enum_chain.rb
@@ -0,0 +1,108 @@
+##
+# Enumerator::Chain test
+
+assert("Enumerable#chain") do
+ a = []
+ b = {}
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a.chain
+ assert_kind_of Enumerator::Chain, a.chain(b)
+ assert_kind_of Enumerator::Chain, a.chain(b, c)
+ assert_raise(NoMethodError) { c.chain }
+end
+
+assert("Enumerator#+") do
+ a = [].each
+ b = {}.each
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a + b
+ assert_kind_of Enumerator::Chain, a + c
+ assert_kind_of Enumerator::Chain, b + a
+ assert_kind_of Enumerator::Chain, b + c
+ assert_raise(NoMethodError) { c + a }
+end
+
+assert("Enumerator::Chain.new") do
+ a = []
+ b = {}
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, a)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, b)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, c)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, a)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, b)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, c)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(c, a)
+end
+
+assert("Enumerator::Chain#each") do
+ a = [1, 2, 3]
+
+ aa = a.chain(a)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 1, 2, 3], aa.each.to_a
+
+ aa = a.chain(6..9)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 6, 7, 8, 9], aa.each.to_a
+
+ aa = a.chain((-3..-2).each_with_index, a)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, [-3, 0], [-2, 1], 1, 2, 3], aa.each.to_a
+
+ aa = a.chain(Object.new)
+ assert_kind_of Enumerator, aa.each
+ assert_raise(NoMethodError) { aa.each.to_a }
+end
+
+assert("Enumerator::Chain#size") do
+ a = [1, 2, 3]
+
+ aa = a.chain(a)
+ assert_equal 6, aa.size
+
+ aa = a.chain(3..4)
+ assert_nil aa.size
+
+ aa = a.chain(3..4, a)
+ assert_nil aa.size
+
+ aa = a.chain(Object.new)
+ assert_nil aa.size
+end
+
+assert("Enumerator::Chain#rewind") do
+ rewound = nil
+ e1 = [1, 2]
+ e2 = (4..6)
+ (class << e1; self end).define_method(:rewind) { rewound << self }
+ (class << e2; self end).define_method(:rewind) { rewound << self }
+ c = e1.chain(e2)
+
+ rewound = []
+ c.rewind
+ assert_equal [], rewound
+
+ rewound = []
+ c.each{break c}.rewind
+ assert_equal [e1], rewound
+
+ rewound = []
+ c.each{}.rewind
+ assert_equal [e2, e1], rewound
+end
+
+assert("Enumerator::Chain#+") do
+ a = [].chain
+ b = {}.chain
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a + b
+ assert_kind_of Enumerator::Chain, a + c
+ assert_kind_of Enumerator::Chain, b + a
+ assert_kind_of Enumerator::Chain, b + c
+end
diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb
index 6cbacdf9e..5f39235e0 100644
--- a/mrbgems/mruby-enum-ext/mrblib/enum.rb
+++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -13,10 +13,9 @@ module Enumerable
# a.drop(3) #=> [4, 5, 0]
def drop(n)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "attempt to drop negative size" if n < 0
- n = n.to_int
ary = []
self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
ary
@@ -57,8 +56,8 @@ module Enumerable
# a.take(3) #=> [1, 2, 3]
def take(n)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
- i = n.to_int
+ n = n.__to_int
+ i = n.to_i
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
@@ -113,12 +112,12 @@ module Enumerable
# [8, 9, 10]
def each_cons(n, &block)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "invalid size" if n <= 0
return to_enum(:each_cons,n) unless block
ary = []
- n = n.to_int
+ n = n.to_i
self.each do |*val|
ary.shift if ary.size == n
ary << val.__svalue
@@ -141,12 +140,12 @@ module Enumerable
# [10]
def each_slice(n, &block)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "invalid slice size" if n <= 0
return to_enum(:each_slice,n) unless block
ary = []
- n = n.to_int
+ n = n.to_i
self.each do |*val|
ary << val.__svalue
if ary.size == n
@@ -201,14 +200,11 @@ module Enumerable
ary.push([block.call(e), i])
}
if ary.size > 1
- ary.__sort_sub__(0, ary.size - 1) do |a,b|
- a <=> b
- end
+ ary.sort!
end
ary.collect{|e,i| orig[i]}
end
- NONE = Object.new
##
# call-seq:
# enum.first -> obj or nil
@@ -225,9 +221,7 @@ module Enumerable
end
return nil
when 1
- n = args[0]
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
- i = n.to_int
+ i = args[0].__to_int
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
@@ -434,7 +428,7 @@ module Enumerable
max = min = val.__svalue
max_cmp = min_cmp = block.call(*val)
first = false
- else
+ else
if (cmp = block.call(*val)) > max_cmp
max = val.__svalue
max_cmp = cmp
@@ -675,13 +669,7 @@ module Enumerable
if nv.nil?
n = -1
else
- unless nv.respond_to?(:to_int)
- raise TypeError, "no implicit conversion of #{nv.class} into Integer"
- end
- n = nv.to_int
- unless n.kind_of?(Integer)
- raise TypeError, "no implicit conversion of #{nv.class} into Integer"
- end
+ n = nv.__to_int
return nil if n <= 0
end
@@ -823,10 +811,6 @@ module Enumerable
h
end
- def nil.to_h
- {}
- end
-
def uniq(&block)
hash = {}
if block
@@ -842,4 +826,34 @@ module Enumerable
end
hash.values
end
+
+ def filter_map(&blk)
+ return to_enum(:filter_map) unless blk
+
+ ary = []
+ self.each do |x|
+ x = blk.call(x)
+ ary.push x if x
+ end
+ ary
+ end
+
+ alias filter select
+
+ ##
+ # call-seq:
+ # enum.tally -> a_hash
+ #
+ # Tallys the collection. Returns a hash where the keys are the
+ # elements and the values are numbers of elements in the collection
+ # that correspond to the key.
+ #
+ # ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1}
+ def tally
+ hash = {}
+ self.each do |x|
+ hash[x] = (hash[x]||0)+1
+ end
+ hash
+ end
end
diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb
index b2d7297a4..31181fe1a 100644
--- a/mrbgems/mruby-enum-ext/test/enum.rb
+++ b/mrbgems/mruby-enum-ext/test/enum.rb
@@ -100,7 +100,6 @@ 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?
@@ -110,32 +109,32 @@ 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?
+ assert_true [nil, true, false].one?(NilClass)
end
assert("Enumerable#all? (enhancement)") do
- assert_false [1, 2, 3.14].all?(Integer)
- assert_true [1, 2, 3.14].all?(Numeric)
+ assert_false [1, 2, nil].all?(Integer)
+ assert_true [1, 2, 3].all?(Numeric)
end
assert("Enumerable#any? (enhancement)") do
- assert_false [1, 2, 3].all?(Float)
assert_true [nil, true, 99].any?(Integer)
+ assert_false [1, 2, 3].any?(Array)
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_equal [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_same r, r.reverse_each { |v| a << v }
assert_equal [3, 2, 1], a
end
@@ -186,8 +185,13 @@ assert("Enumerable#to_h") do
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
+
+assert("Enumerable#filter_map") do
+ assert_equal [4, 8, 12, 16, 20], (1..10).filter_map{|i| i * 2 if i%2==0}
+end
+
+assert("Enumerable#tally") do
+ assert_equal({"a"=>1, "b"=>2, "c"=>1}, ["a", "b", "c", "b"].tally)
+end
diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
index 9227abe8a..e4f116a93 100644
--- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
+++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
@@ -44,7 +44,7 @@ class Enumerator
def to_enum(meth=:each, *args, &block)
unless self.respond_to?(meth)
- raise NoMethodError, "undefined method #{meth}"
+ raise ArgumentError, "undefined method #{meth}"
end
lz = Lazy.new(self, &block)
lz.obj = self
diff --git a/mrbgems/mruby-enum-lazy/test/lazy.rb b/mrbgems/mruby-enum-lazy/test/lazy.rb
index 1a55d30cf..e8277f3d3 100644
--- a/mrbgems/mruby-enum-lazy/test/lazy.rb
+++ b/mrbgems/mruby-enum-lazy/test/lazy.rb
@@ -41,7 +41,7 @@ assert("Enumerator::Lazy laziness") do
end
assert("Enumerator::Lazy#to_enum") do
- lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2)
+ lazy_enum = (0..).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
diff --git a/mrbgems/mruby-enumerator/mrbgem.rake b/mrbgems/mruby-enumerator/mrbgem.rake
index 8757a15ea..abcc54e7a 100644
--- a/mrbgems/mruby-enumerator/mrbgem.rake
+++ b/mrbgems/mruby-enumerator/mrbgem.rake
@@ -2,6 +2,5 @@ MRuby::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
diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
index 7ca1d5eb6..f007b8553 100644
--- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb
+++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -89,7 +89,6 @@ 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
@@ -109,27 +108,31 @@ class Enumerator
#
# p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
#
- def initialize(obj=nil, meth=:each, *args, &block)
+ # In the second, deprecated, form, a generated Enumerator iterates over the
+ # given object using the given method with the given arguments passed. This
+ # form is left only for internal use.
+ #
+ # Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum
+ # instead.
+ def initialize(obj=NONE, 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}"
+ elsif obj == NONE
+ raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
end
@obj = obj
@meth = meth
- @args = args.dup
+ @args = args
@fib = nil
@dst = nil
@lookahead = nil
@feedvalue = nil
@stop_exc = false
end
- attr_accessor :obj, :meth, :args, :fib
- private :obj, :meth, :args, :fib
+
+ attr_accessor :obj, :meth, :args
+ attr_reader :fib
def initialize_copy(obj)
raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
@@ -157,12 +160,10 @@ class Enumerator
def with_index(offset=0, &block)
return to_enum :with_index, offset unless block
- offset = if offset.nil?
- 0
- elsif offset.respond_to?(:to_int)
- offset.to_int
+ if offset.nil?
+ offset = 0
else
- raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+ offset = offset.__to_int
end
n = offset - 1
@@ -223,13 +224,11 @@ class Enumerator
end
def inspect
- return "#<#{self.class}: uninitialized>" unless @obj
-
if @args && @args.size > 0
args = @args.join(", ")
- "#<#{self.class}: #{@obj}:#{@meth}(#{args})>"
+ "#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>"
else
- "#<#{self.class}: #{@obj}:#{@meth}>"
+ "#<#{self.class}: #{@obj.inspect}:#{@meth}>"
end
end
@@ -245,9 +244,10 @@ class Enumerator
#
# === 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"]
+ # Array.new(3) #=> [nil, nil, nil]
+ # Array.new(3) { |i| i } #=> [0, 1, 2]
+ # Array.to_enum(:new, 3).to_a #=> [0, 1, 2]
+ # Array.to_enum(:new).each(3).to_a #=> [0, 1, 2]
#
# obj = Object.new
#
@@ -556,6 +556,46 @@ class Enumerator
self
end
end
+
+ ##
+ # call-seq:
+ # Enumerator.produce(initial = nil) { |val| } -> enumerator
+ #
+ # Creates an infinite enumerator from any block, just called over and
+ # over. Result of the previous iteration is passed to the next one.
+ # If +initial+ is provided, it is passed to the first iteration, and
+ # becomes the first element of the enumerator; if it is not provided,
+ # first iteration receives +nil+, and its result becomes first
+ # element of the iterator.
+ #
+ # Raising StopIteration from the block stops an iteration.
+ #
+ # Examples of usage:
+ #
+ # Enumerator.produce(1, &:succ) # => enumerator of 1, 2, 3, 4, ....
+ #
+ # Enumerator.produce { rand(10) } # => infinite random number sequence
+ #
+ # ancestors = Enumerator.produce(node) { |prev| node = prev.parent or raise StopIteration }
+ # enclosing_section = ancestors.find { |n| n.type == :section }
+ def Enumerator.produce(init=NONE, &block)
+ raise ArgumentError, "no block given" if block.nil?
+ Enumerator.new do |y|
+ if init == NONE
+ val = nil
+ else
+ val = init
+ y.yield(val)
+ end
+ begin
+ while true
+ y.yield(val = block.call(val))
+ end
+ rescue StopIteration
+ # do nothing
+ end
+ end
+ end
end
module Kernel
@@ -563,15 +603,10 @@ 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"
@@ -589,17 +624,14 @@ module Kernel
# 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:
+ # Here is such an example with parameter passing:
#
# 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
+ # return to_enum(__method__, n) # __method__ is :repeat here
# end
# each do |*val|
# n.times { yield *val }
@@ -623,9 +655,7 @@ module Enumerable
# use Enumerator to use infinite sequence
def zip(*args, &block)
args = args.map do |a|
- if a.respond_to?(:to_ary)
- a.to_ary.to_enum(:each)
- elsif a.respond_to?(:each)
+ if a.respond_to?(:each)
a.to_enum(:each)
else
raise TypeError, "wrong argument type #{a.class} (must respond to :each)"
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index 428ea0307..3e0c6c3be 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -6,11 +6,22 @@ class << @obj
end
end
-assert 'Enumerator' do
+def assert_take(exp, enumerator)
+ result = []
+ n = exp.size
+ enumerator.each do |v|
+ result << v
+ n -= 1
+ break if n == 0
+ end if n > 0
+ assert_equal exp, result
+end
+
+assert 'Enumerator.class' do
assert_equal Class, Enumerator.class
end
-assert 'Enumerator' do
+assert 'Enumerator.superclass' do
assert_equal Object, Enumerator.superclass
end
@@ -19,11 +30,8 @@ assert 'Enumerator.new' do
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_take [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }
assert_raise(ArgumentError) { Enumerator.new }
- enum = @obj.to_enum
- assert_raise(NoMethodError) { enum.each {} }
# examples
fib = Enumerator.new do |y|
@@ -33,7 +41,7 @@ assert 'Enumerator.new' do
a, b = b, a + b
end
end
- assert_equal [1,1,2,3,5,8,13,21,34,55], fib.take(10)
+ assert_take [1,1,2,3,5,8,13,21,34,55], fib
end
assert 'Enumerator#initialize_copy' do
@@ -55,12 +63,6 @@ assert 'Enumerator#with_index' do
assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
end
-assert 'Enumerator#with_index nonnum offset' do
- s = Object.new
- def s.to_int; 1 end
- assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_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
@@ -99,11 +101,13 @@ end
assert 'Enumerator#inspect' do
e = (0..10).each
- assert_equal("#<Enumerator: 0..10:each>", e.inspect)
- e = Enumerator.new("FooObject", :foo, 1)
- assert_equal("#<Enumerator: FooObject:foo(1)>", e.inspect)
- e = Enumerator.new("FooObject", :foo, 1, 2, 3)
- assert_equal("#<Enumerator: FooObject:foo(1, 2, 3)>", e.inspect)
+ assert_equal('#<Enumerator: 0..10:each>', e.inspect)
+ e = 'FooObject'.enum_for(:foo, 1)
+ assert_equal('#<Enumerator: "FooObject":foo(1)>', e.inspect)
+ e = 'FooObject'.enum_for(:foo, 1, 2, 3)
+ assert_equal('#<Enumerator: "FooObject":foo(1, 2, 3)>', e.inspect)
+ e = nil.enum_for(:to_s)
+ assert_equal('#<Enumerator: nil:to_s>', e.inspect)
end
assert 'Enumerator#each' do
@@ -425,8 +429,10 @@ assert 'nested iteration' do
end
assert 'Kernel#to_enum' do
+ e = nil
assert_equal Enumerator, [].to_enum.class
- assert_raise(ArgumentError){ nil.to_enum }
+ assert_nothing_raised { e = [].to_enum(:_not_implemented_) }
+ assert_raise(NoMethodError) { e.first }
end
assert 'modifying existing methods' do
@@ -439,7 +445,7 @@ assert 'modifying existing methods' do
}
end
-assert 'Integral#times' do
+assert 'Integer#times' do
a = 3
b = a.times
c = []
@@ -546,11 +552,49 @@ assert 'Range#each' do
end
assert 'Enumerable#zip' do
- assert_equal [[1, 10], [2, 11], [3, 12]], [1,2,3].zip(10..Float::INFINITY)
+ assert_equal [[1, 10], [2, 11], [3, 12]], [1,2,3].zip(10..)
ret = []
- assert_equal nil, [1,2,3].zip(10..Float::INFINITY) { |i| ret << i }
+ assert_equal nil, [1,2,3].zip(10..) { |i| ret << i }
assert_equal [[1, 10], [2, 11], [3, 12]], ret
assert_raise(TypeError) { [1].zip(1) }
end
+
+assert 'Enumerator.produce' do
+ assert_raise(ArgumentError) { Enumerator.produce }
+
+ # Without initial object
+ passed_args = []
+ enum = Enumerator.produce {|obj| passed_args << obj; (obj || 0).succ }
+ assert_equal Enumerator, enum.class
+ assert_take [1, 2, 3], enum
+ assert_equal [nil, 1, 2], passed_args
+
+ # With initial object
+ passed_args = []
+ enum = Enumerator.produce(1) {|obj| passed_args << obj; obj.succ }
+ assert_take [1, 2, 3], enum
+ assert_equal [1, 2], passed_args
+
+ # Raising StopIteration
+ words = %w[The quick brown fox jumps over the lazy dog]
+ enum = Enumerator.produce { words.shift or raise StopIteration }
+ assert_equal %w[The quick brown fox jumps over the lazy dog], enum.to_a
+
+ # Raising StopIteration
+ object = [[[["abc", "def"], "ghi", "jkl"], "mno", "pqr"], "stuv", "wxyz"]
+ enum = Enumerator.produce(object) {|obj|
+ obj.respond_to?(:first) or raise StopIteration
+ obj.first
+ }
+ assert_nothing_raised {
+ assert_equal [
+ [[[["abc", "def"], "ghi", "jkl"], "mno", "pqr"], "stuv", "wxyz"],
+ [[["abc", "def"], "ghi", "jkl"], "mno", "pqr"],
+ [["abc", "def"], "ghi", "jkl"],
+ ["abc", "def"],
+ "abc",
+ ], enum.to_a
+ }
+end
diff --git a/mrbgems/mruby-error/mrbgem.rake b/mrbgems/mruby-error/mrbgem.rake
index 30a4259a8..fe9062089 100644
--- a/mrbgems/mruby-error/mrbgem.rake
+++ b/mrbgems/mruby-error/mrbgem.rake
@@ -2,9 +2,4 @@ MRuby::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
diff --git a/mrbgems/mruby-error/src/exception.c b/mrbgems/mruby-error/src/exception.c
index 170abb699..ffa7d53bf 100644
--- a/mrbgems/mruby-error/src/exception.c
+++ b/mrbgems/mruby-error/src/exception.c
@@ -1,50 +1,38 @@
#include <mruby.h>
-#include <mruby/throw.h>
#include <mruby/error.h>
+struct protect_data {
+ mrb_func_t body;
+ mrb_value data;
+};
+
+static mrb_value
+protect_body(mrb_state *mrb, void *p)
+{
+ struct protect_data *dp = (struct protect_data*)p;
+ return dp->body(mrb, dp->data);
+}
+
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;
+ struct protect_data protect_data = { body, data };
+ return mrb_protect_error(mrb, protect_body, &protect_data, state);
}
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);
-
+ int ai = mrb_gc_arena_save(mrb);
+ struct protect_data protect_data = { body, b_data };
+ mrb_bool error;
+ mrb_value result = mrb_protect_error(mrb, protect_body, &protect_data, &error);
ensure(mrb, e_data);
+ mrb_gc_arena_restore(mrb, ai);
mrb_gc_protect(mrb, result);
+ if (error) {
+ mrb_exc_raise(mrb, result); /* rethrow caught exceptions */
+ }
return result;
}
@@ -59,33 +47,26 @@ 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])) {
+ int ai = mrb_gc_arena_save(mrb);
+ struct protect_data protect_data = { body, b_data };
+ mrb_bool error;
+ mrb_value result = mrb_protect_error(mrb, protect_body, &protect_data, &error);
+ if (error) {
+ mrb_bool error_matched = FALSE;
+ for (mrb_int i = 0; i < len; ++i) {
+ if (mrb_obj_is_kind_of(mrb, result, classes[i])) {
error_matched = TRUE;
break;
}
}
- if (!error_matched) { MRB_THROW(mrb->jmp); }
+ if (!error_matched) { mrb_exc_raise(mrb, result); }
mrb->exc = NULL;
result = rescue(mrb, r_data);
- } MRB_END_EXC(&c_jmp);
-
- mrb_gc_protect(mrb, result);
+ mrb_gc_arena_restore(mrb, ai);
+ mrb_gc_protect(mrb, result);
+ }
return result;
}
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index e6a82723d..de1bcd02d 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -5,248 +5,68 @@
#include <mruby/proc.h>
#include <mruby/opcode.h>
#include <mruby/error.h>
+#include <mruby/presym.h>
+#include <mruby/variable.h>
+struct REnv *mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc);
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; i<top->rlen; 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+1);
- i += mrb_insn_size1[insn];
- continue;
- case OP_EXT2:
- insn = PEEK_B(irep->iseq+1);
- i += mrb_insn_size2[insn];
- continue;
- case OP_EXT3:
- insn = PEEK_B(irep->iseq+1);
- i += mrb_insn_size3[insn];
- continue;
- }
- i+=mrb_insn_size[insn];
- }
-}
-
+mrb_value mrb_mod_module_eval(mrb_state*, mrb_value);
void mrb_codedump_all(mrb_state*, struct RProc*);
+void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack);
static struct RProc*
-create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding, const char *file, mrb_int line)
+create_proc_from_string(mrb_state *mrb, const 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;
+ const struct RProc *scope;
struct REnv *e;
mrb_callinfo *ci; /* callinfo of eval caller */
struct RClass *target_class = NULL;
- int bidx;
+ struct mrb_context *c = mrb->c;
if (!mrb_nil_p(binding)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil.");
+ mrb_value scope_obj;
+ if (!mrb_class_defined_id(mrb, MRB_SYM(Binding))
+ || !mrb_obj_is_kind_of(mrb, binding, mrb_class_get_id(mrb, MRB_SYM(Binding)))) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %C (expected binding)",
+ mrb_obj_class(mrb, binding));
+ }
+ scope_obj = mrb_iv_get(mrb, binding, MRB_SYM(proc));
+ mrb_assert(mrb_proc_p(scope_obj));
+ scope = mrb_proc_ptr(scope_obj);
+ if (MRB_PROC_CFUNC_P(scope)) {
+ e = NULL;
+ }
+ else {
+ mrb_value env = mrb_iv_get(mrb, binding, MRB_SYM(env));
+ mrb_assert(mrb_env_p(env));
+ e = (struct REnv *)mrb_obj_ptr(env);
+ mrb_assert(e != NULL);
+ }
+ }
+ else {
+ ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase;
+ scope = ci->proc;
+ e = NULL;
}
cxt = mrbc_context_new(mrb);
- cxt->lineno = (short)line;
+ cxt->lineno = (uint16_t)line;
mrbc_filename(mrb, cxt, file ? file : "(eval)");
cxt->capture_errors = TRUE;
cxt->no_optimize = TRUE;
- cxt->on_eval = TRUE;
+ cxt->upper = scope && MRB_PROC_CFUNC_P(scope) ? NULL : scope;
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.");
+ mrbc_context_free(mrb, cxt);
+ mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state (out of memory)");
}
if (0 < p->nerr) {
@@ -254,15 +74,15 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
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));
+ str = mrb_format(mrb, "file %s line %d: %s",
+ file,
+ p->error_buffer[0].lineno,
+ 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));
+ str = mrb_format(mrb, "line %d: %s",
+ p->error_buffer[0].lineno,
+ p->error_buffer[0].message);
}
mrb_parser_free(p);
mrbc_context_free(mrb, cxt);
@@ -276,39 +96,30 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
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];
+ if (c->ci > c->cibase) {
+ ci = &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;
+ ci = c->cibase;
+ }
+ if (scope) {
+ target_class = MRB_PROC_TARGET_CLASS(scope);
+ if (!MRB_PROC_CFUNC_P(scope)) {
+ if (e == NULL) {
+ /* when `binding` is nil */
+ e = mrb_vm_ci_env(ci);
+ if (e == NULL) {
+ e = mrb_env_new(mrb, c, ci, ci->proc->body.irep->nlocals, ci->stack, target_class);
+ ci->u.env = e;
+ }
+ }
+ proc->e.env = e;
+ proc->flags |= MRB_PROC_ENVSET;
+ mrb_field_write_barrier(mrb, (struct RBasic*)proc, (struct RBasic*)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);
+ proc->upper = scope;
+ mrb_vm_ci_target_class_set(mrb->c->ci, target_class);
/* mrb_codedump_all(mrb, proc); */
mrb_parser_free(p);
@@ -322,33 +133,27 @@ 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();
+ mrb->c->ci->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;
+ const char *s;
mrb_int len;
mrb_value binding = mrb_nil_value();
- char *file = NULL;
+ const 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);
+ if (!mrb_nil_p(binding)) {
+ self = mrb_iv_get(mrb, binding, MRB_SYM(recv));
+ }
mrb_assert(!MRB_PROC_CFUNC_P(proc));
return exec_irep(mrb, self, proc);
}
@@ -356,15 +161,10 @@ f_eval(mrb_state *mrb, mrb_value self)
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;
+ if (!mrb_block_given_p(mrb)) {
+ const char *s;
mrb_int len;
- char *file = NULL;
+ const char *file = NULL;
mrb_int line = 1;
mrb_value cv;
struct RProc *proc;
@@ -374,20 +174,45 @@ f_instance_eval(mrb_state *mrb, mrb_value 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);
+ mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(cv));
return exec_irep(mrb, self, proc);
}
else {
- mrb_get_args(mrb, "&", &b);
+ mrb_get_args(mrb, "");
return mrb_obj_instance_eval(mrb, self);
}
}
+static mrb_value
+f_class_eval(mrb_state *mrb, mrb_value self)
+{
+ if (!mrb_block_given_p(mrb)) {
+ const char *s;
+ mrb_int len;
+ const char *file = NULL;
+ mrb_int line = 1;
+ struct RProc *proc;
+
+ mrb_get_args(mrb, "s|zi", &s, &len, &file, &line);
+ proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
+ MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(self));
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(self));
+ return exec_irep(mrb, self, proc);
+ }
+ else {
+ mrb_get_args(mrb, "");
+ return mrb_mod_module_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));
+ mrb_define_method_id(mrb, mrb_class_get_id(mrb, MRB_SYM(BasicObject)), MRB_SYM(instance_eval), f_instance_eval, MRB_ARGS_OPT(3)|MRB_ARGS_BLOCK());
+ mrb_define_method_id(mrb, mrb_class_get_id(mrb, MRB_SYM(Module)), MRB_SYM(module_eval), f_class_eval, MRB_ARGS_OPT(3)|MRB_ARGS_BLOCK());
+ mrb_define_method_id(mrb, mrb_class_get_id(mrb, MRB_SYM(Module)), MRB_SYM(class_eval), f_class_eval, MRB_ARGS_OPT(3)|MRB_ARGS_BLOCK());
}
void
diff --git a/mrbgems/mruby-eval/test/eval.rb b/mrbgems/mruby-eval/test/eval.rb
index 4d7dd4606..524b0b959 100644
--- a/mrbgems/mruby-eval/test/eval.rb
+++ b/mrbgems/mruby-eval/test/eval.rb
@@ -44,7 +44,7 @@ assert('Kernel#eval', '15.3.1.3.12') do
end
assert('rest arguments of eval') do
- assert_raise(ArgumentError) { Kernel.eval('0', 0, 'test', 0) }
+ assert_raise(TypeError) { Kernel.eval('0', 0, 'test', 0) }
assert_equal ['test', 'test.rb', 10] do
Kernel.eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10)
end
@@ -80,7 +80,7 @@ assert('Kernel.#eval(string) context') do
assert_equal('class') { obj.const_string }
end
-assert('Object#instance_eval with begin-rescue-ensure execution order') do
+assert('BasicObject#instance_eval with begin-rescue-ensure execution order') do
class HellRaiser
def raise_hell
order = [:enter_raise_hell]
@@ -100,7 +100,7 @@ assert('Object#instance_eval with begin-rescue-ensure execution order') do
assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
end
-assert('Kernel#instance_eval() to define singleton methods Issue #3141') do
+assert('BasicObject#instance_eval to define singleton methods Issue #3141') do
foo_class = Class.new do
def bar(x)
instance_eval "def baz; #{x}; end"
@@ -130,3 +130,32 @@ Proc.new { foo }
EOS
}
end
+
+assert('Calling the same method as the variable name') do
+ hoge = Object.new
+ def hoge.fuga
+ "Hit!"
+ end
+ assert_equal("Hit!") { fuga = "Miss!"; eval "hoge.fuga" }
+ assert_equal("Hit!") { fuga = "Miss!"; -> { eval "hoge.fuga" }.call }
+ assert_equal("Hit!") { -> { fuga = "Miss!"; eval "hoge.fuga" }.call }
+ assert_equal("Hit!") { fuga = "Miss!"; eval("-> { hoge.fuga }").call }
+end
+
+assert('Access numbered parameter from eval') do
+ hoge = Object.new
+ def hoge.fuga(a, &b)
+ b.call(a)
+ end
+ assert_equal(6) {
+ hoge.fuga(3) { _1 + eval("_1") }
+ }
+end
+
+assert('Module#class_eval with string') do
+ c = Class.new
+ c.class_eval "def foo() 42; end"
+ cc = c.new
+ assert_true cc.respond_to?(:foo)
+ assert_equal 42, c.new.foo
+end
diff --git a/mrbgems/mruby-exit/src/mruby-exit.c b/mrbgems/mruby-exit/src/mruby-exit.c
index 05c929622..4ff2f932c 100644
--- a/mrbgems/mruby-exit/src/mruby-exit.c
+++ b/mrbgems/mruby-exit/src/mruby-exit.c
@@ -4,12 +4,17 @@
static mrb_value
f_exit(mrb_state *mrb, mrb_value self)
{
- mrb_int i = EXIT_SUCCESS;
+ mrb_value status = mrb_true_value();
+ int istatus;
+
+ mrb_get_args(mrb, "|o", &status);
+ istatus = mrb_true_p(status) ? EXIT_SUCCESS :
+ mrb_false_p(status) ? EXIT_FAILURE :
+ (int)mrb_int(mrb, status);
+ exit(istatus);
- mrb_get_args(mrb, "|i", &i);
- exit((int)i);
/* not reached */
- return mrb_nil_value();
+ return status;
}
void
diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c
index 17ce77c5d..3e6a620be 100644
--- a/mrbgems/mruby-fiber/src/fiber.c
+++ b/mrbgems/mruby-fiber/src/fiber.c
@@ -72,14 +72,11 @@ fiber_init(mrb_state *mrb, mrb_value self)
mrb_value blk;
size_t slen;
- mrb_get_args(mrb, "&", &blk);
+ 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");
@@ -96,7 +93,6 @@ fiber_init(mrb_state *mrb, mrb_value self)
}
c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value));
c->stend = c->stbase + slen;
- c->stack = c->stbase;
#ifdef MRB_NAN_BOXING
{
@@ -113,20 +109,19 @@ fiber_init(mrb_state *mrb, mrb_value self)
#endif
/* copy receiver from a block */
- c->stack[0] = mrb->c->stack[0];
+ c->stbase[0] = mrb->c->ci->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_vm_ci_target_class_set(ci, MRB_PROC_TARGET_CLASS(p));
+ mrb_vm_ci_proc_set(ci, p);
mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p);
- ci->pc = p->body.irep->iseq;
+ ci->stack = c->stbase;
ci[1] = ci[0];
c->ci++; /* push dummy callinfo */
@@ -157,7 +152,7 @@ fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len)
}
/* mark return from context modifying method */
-#define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL
+#define MARK_CONTEXT_MODIFY(c) (c)->ci->u.target_class = NULL
static void
fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
@@ -191,14 +186,21 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
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) {
+ switch (status) {
+ case MRB_FIBER_TRANSFERRED:
+ if (resume) {
+ mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
+ }
+ break;
+ case MRB_FIBER_RUNNING:
+ case MRB_FIBER_RESUMED:
mrb_raise(mrb, E_FIBER_ERROR, "double resume");
- }
- if (status == MRB_FIBER_TERMINATED) {
+ break;
+ case MRB_FIBER_TERMINATED:
mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
+ break;
+ default:
+ break;
}
old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
@@ -210,21 +212,27 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
mrb_raise(mrb, E_FIBER_ERROR, "double resume (current)");
}
mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
- b = c->stack+1;
+ b = c->stbase+1;
e = b + len;
while (b<e) {
*b++ = *a++;
}
+ if (vmexec) {
+ c->ci--; /* pop dummy callinfo */
+ }
c->cibase->argc = (int)len;
- value = c->stack[0] = MRB_PROC_ENV(c->ci->proc)->stack[0];
+ value = c->stbase[0] = MRB_PROC_ENV(c->cibase->proc)->stack[0];
}
else {
value = fiber_result(mrb, a, len);
+ if (vmexec) {
+ c->ci->stack[c->ci[1].acc] = value;
+ }
}
if (vmexec) {
c->vmexec = TRUE;
- value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc);
+ value = mrb_vm_exec(mrb, c->ci->proc, c->ci->pc);
mrb->c = old_c;
}
else {
@@ -251,7 +259,7 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
static mrb_value
fiber_resume(mrb_state *mrb, mrb_value self)
{
- mrb_value *a;
+ const mrb_value *a;
mrb_int len;
mrb_bool vmexec = FALSE;
@@ -287,10 +295,9 @@ mrb_fiber_alive_p(mrb_state *mrb, mrb_value self)
static mrb_value
fiber_eq(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
- mrb_get_args(mrb, "o", &other);
+ mrb_value other = mrb_get_arg1(mrb);
- if (mrb_type(other) != MRB_TT_FIBER) {
+ if (!mrb_fiber_p(other)) {
return mrb_false_value();
}
return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other));
@@ -312,7 +319,7 @@ static mrb_value
fiber_transfer(mrb_state *mrb, mrb_value self)
{
struct mrb_context *c = fiber_check(mrb, self);
- mrb_value* a;
+ const mrb_value* a;
mrb_int len;
fiber_check_cfunc(mrb, mrb->c);
@@ -351,6 +358,7 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
if (c->vmexec) {
c->vmexec = FALSE;
mrb->c->ci->acc = CI_ACC_RESUMED;
+ c->ci--; /* pop callinfo for yield */
}
MARK_CONTEXT_MODIFY(mrb->c);
return fiber_result(mrb, a, len);
@@ -371,7 +379,7 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
static mrb_value
fiber_yield(mrb_state *mrb, mrb_value self)
{
- mrb_value *a;
+ const mrb_value *a;
mrb_int len;
mrb_get_args(mrb, "*!", &a, &len);
@@ -389,7 +397,7 @@ 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));
+ struct RFiber *f = MRB_OBJ_ALLOC(mrb, MRB_TT_FIBER, mrb_class_ptr(self));
f->cxt = mrb->c;
mrb->c->fib = f;
@@ -405,7 +413,7 @@ mrb_mruby_fiber_gem_init(mrb_state* mrb)
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, "initialize", fiber_init, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
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());
diff --git a/mrbgems/mruby-fiber/test/fiber.rb b/mrbgems/mruby-fiber/test/fiber.rb
index d063a0a62..b6ecb798c 100644
--- a/mrbgems/mruby-fiber/test/fiber.rb
+++ b/mrbgems/mruby-fiber/test/fiber.rb
@@ -76,7 +76,7 @@ assert('Fiber iteration') do
end
assert('Fiber with splat in the block argument list') {
- Fiber.new{|*x|x}.resume(1) == [1]
+ assert_equal([1], Fiber.new{|*x|x}.resume(1))
}
assert('Fiber raises on resume when dead') do
diff --git a/mrbgems/mruby-hash-ext/mrbgem.rake b/mrbgems/mruby-hash-ext/mrbgem.rake
index 103410ab9..e0681c1cb 100644
--- a/mrbgems/mruby-hash-ext/mrbgem.rake
+++ b/mrbgems/mruby-hash-ext/mrbgem.rake
@@ -2,7 +2,5 @@ MRuby::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
diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb
index 5bbbdf559..5775913c3 100644
--- a/mrbgems/mruby-hash-ext/mrblib/hash.rb
+++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb
@@ -27,9 +27,9 @@ class Hash
length = object.length
if length == 1
o = object[0]
- if o.respond_to?(:to_hash)
+ if Hash === o
h = self.new
- object[0].to_hash.each { |k, v| h[k] = v }
+ o.each { |k, v| h[k] = v }
return h
elsif o.respond_to?(:to_a)
h = self.new
@@ -62,25 +62,6 @@ class Hash
##
# call-seq:
- # Hash.try_convert(obj) -> hash or nil
- #
- # Try to convert <i>obj</i> into a hash, using to_hash method.
- # Returns converted hash or nil if <i>obj</i> cannot be converted
- # for any reason.
- #
- # Hash.try_convert({1=>2}) # => {1=>2}
- # Hash.try_convert("1=>2") # => nil
- #
- def self.try_convert(obj)
- if obj.respond_to?(:to_hash)
- obj.to_hash
- else
- nil
- end
- end
-
- ##
- # call-seq:
# hsh.merge!(other_hash) -> hsh
# hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh
#
@@ -101,7 +82,7 @@ class Hash
#
def merge!(other, &block)
- raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
+ 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]
@@ -135,7 +116,6 @@ class Hash
nk.each {|k|
h[k] = self[k]
}
- h
self.replace(h)
end
@@ -275,7 +255,6 @@ class Hash
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)
@@ -329,11 +308,7 @@ class Hash
# h1 < h1 #=> false
#
def <(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ 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
}
@@ -353,11 +328,7 @@ class Hash
# h1 <= h1 #=> true
#
def <=(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ 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
}
@@ -377,11 +348,7 @@ class Hash
# h1 > h1 #=> false
#
def >(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ 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
}
@@ -401,11 +368,7 @@ class Hash
# h1 >= h1 #=> true
#
def >=(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ 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
}
@@ -529,4 +492,7 @@ class Hash
self.fetch(k, &block)
end
end
+
+ alias filter select
+ alias filter! select!
end
diff --git a/mrbgems/mruby-hash-ext/src/hash-ext.c b/mrbgems/mruby-hash-ext/src/hash-ext.c
index e6112667b..9c85858fe 100644
--- a/mrbgems/mruby-hash-ext/src/hash-ext.c
+++ b/mrbgems/mruby-hash-ext/src/hash-ext.c
@@ -22,7 +22,8 @@
static mrb_value
hash_values_at(mrb_state *mrb, mrb_value hash)
{
- mrb_value *argv, result;
+ const mrb_value *argv;
+ mrb_value result;
mrb_int argc, i;
int ai;
@@ -49,14 +50,13 @@ hash_values_at(mrb_state *mrb, mrb_value hash)
static mrb_value
hash_slice(mrb_state *mrb, mrb_value hash)
{
- mrb_value *argv, result;
+ const mrb_value *argv;
+ mrb_value 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);
+ if (argc == 0) return result; /* empty hash */
for (i = 0; i < argc; i++) {
mrb_value key = argv[i];
mrb_value val;
@@ -69,6 +69,31 @@ hash_slice(mrb_state *mrb, mrb_value hash)
return result;
}
+/*
+ * call-seq:
+ * hsh.except(*keys) -> a_hash
+ *
+ * Returns a hash excluding the given keys and their values.
+ *
+ * h = { a: 100, b: 200, c: 300 }
+ * h.except(:a) #=> {:b=>200, :c=>300}
+ * h.except(:b, :c, :d) #=> {:a=>100}
+ */
+static mrb_value
+hash_except(mrb_state *mrb, mrb_value hash)
+{
+ const mrb_value *argv;
+ mrb_value result;
+ mrb_int argc, i;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ result = mrb_hash_dup(mrb, hash);
+ for (i = 0; i < argc; i++) {
+ mrb_hash_delete_key(mrb, result, argv[i]);
+ }
+ return result;
+}
+
void
mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
{
@@ -77,6 +102,7 @@ mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
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());
+ mrb_define_method(mrb, h, "except", hash_except, MRB_ARGS_ANY());
}
void
diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb
index 269da800d..78e8b3a3a 100644
--- a/mrbgems/mruby-hash-ext/test/hash.rb
+++ b/mrbgems/mruby-hash-ext/test/hash.rb
@@ -45,12 +45,6 @@ assert('Hash.[] for sub class') do
assert_equal(sub_hash_class, sub_hash.class)
end
-assert('Hash.try_convert') do
- assert_nil Hash.try_convert(nil)
- assert_nil Hash.try_convert("{1=>2}")
- assert_equal({1=>2}, Hash.try_convert({1=>2}))
-end
-
assert('Hash#merge!') do
a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' }
@@ -275,10 +269,8 @@ assert("Hash#transform_keys") do
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})
+ assert_same(h, h.transform_keys!{|k|k.to_i})
+ assert_equal({1 => 100, 2 => 200}, h)
end
assert("Hash#transform_values") do
@@ -287,9 +279,7 @@ assert("Hash#transform_values") do
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_same(h, h.transform_values!{|v|v.to_s})
assert_equal({a: "1", b: "2", c: "3"}, h)
end
@@ -298,3 +288,10 @@ assert("Hash#slice") do
assert_equal({:a=>100}, h.slice(:a))
assert_equal({:b=>200, :c=>300}, h.slice(:b, :c, :d))
end
+
+assert("Hash#except") do
+ h = { a: 100, b: 200, c: 300 }
+ assert_equal({:b=>200, :c=>300}, h.except(:a))
+ assert_equal({:a=>100}, h.except(:b, :c, :d))
+ assert_equal(h, h.except)
+end
diff --git a/mrbgems/mruby-inline-struct/test/inline.c b/mrbgems/mruby-inline-struct/test/inline.c
index 91c767f30..6764b1af4 100644
--- a/mrbgems/mruby-inline-struct/test/inline.c
+++ b/mrbgems/mruby-inline-struct/test/inline.c
@@ -8,15 +8,16 @@ 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);
+ mrb_value object = mrb_get_arg1(mrb);
- if (mrb_float_p(object)) {
- strncpy(string, "float", size-1);
- }
- else if (mrb_fixnum_p(object)) {
+ if (mrb_integer_p(object)) {
strncpy(string, "fixnum", size-1);
}
+#ifndef MRB_NO_FLOAT
+ else if (mrb_float_p(object)) {
+ strncpy(string, "float", size-1);
+ }
+#endif
else if (mrb_string_p(object)) {
strncpy(string, "string", size-1);
}
@@ -43,8 +44,8 @@ istruct_test_length(mrb_state *mrb, mrb_value self)
static mrb_value
istruct_test_test_receive(mrb_state *mrb, mrb_value self)
{
- mrb_value object;
- mrb_get_args(mrb, "o", &object);
+ mrb_value object = mrb_get_arg1(mrb);
+
if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
{
mrb_raise(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
diff --git a/mrbgems/mruby-io/.gitignore b/mrbgems/mruby-io/.gitignore
deleted file mode 100644
index ceeb05b41..000000000
--- a/mrbgems/mruby-io/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/tmp
diff --git a/mrbgems/mruby-io/.travis.yml b/mrbgems/mruby-io/.travis.yml
deleted file mode 100644
index ffe227284..000000000
--- a/mrbgems/mruby-io/.travis.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-script:
- - "ruby run_test.rb all test"
diff --git a/mrbgems/mruby-io/README.md b/mrbgems/mruby-io/README.md
index 256fb8195..c07a6e89b 100644
--- a/mrbgems/mruby-io/README.md
+++ b/mrbgems/mruby-io/README.md
@@ -1,21 +1,20 @@
-mruby-io
-========
-[![Build Status](https://travis-ci.org/iij/mruby-io.svg?branch=master)](https://travis-ci.org/iij/mruby-io)
-
+# mruby-io
`IO` and `File` classes for mruby
## Installation
-Add the line below to your `build_config.rb`:
+
+Add the line below to your build configuration.
```
- conf.gem :github => 'iij/mruby-io'
+ conf.gem core: 'mruby-io'
```
## Implemented methods
### IO
- - http://doc.ruby-lang.org/ja/1.9.3/class/IO.html
+
+* <https://doc.ruby-lang.org/ja/1.9.3/class/IO.html>
| method | mruby-io | memo |
| ------------------------- | -------- | ---- |
@@ -59,7 +58,7 @@ Add the line below to your `build_config.rb`:
| IO#fileno, IO#to_i | o | |
| IO#flush | o | |
| IO#fsync | | |
-| IO#getbyte | | |
+| IO#getbyte | o | |
| IO#getc | o | |
| IO#gets | o | |
| IO#internal_encoding | | |
@@ -77,7 +76,7 @@ Add the line below to your `build_config.rb`:
| IO#puts | o | |
| IO#read | o | |
| IO#read_nonblock | | |
-| IO#readbyte | | |
+| IO#readbyte | o | |
| IO#readchar | o | |
| IO#readline | o | |
| IO#readlines | o | |
@@ -93,13 +92,14 @@ Add the line below to your `build_config.rb`:
| IO#sysseek | o | |
| IO#syswrite | o | |
| IO#to_io | | |
-| IO#ungetbyte | | |
+| IO#ungetbyte | o | |
| IO#ungetc | o | |
| IO#write | o | |
| IO#write_nonblock | | |
### File
- - http://doc.ruby-lang.org/ja/1.9.3/class/File.html
+
+* <https://doc.ruby-lang.org/ja/1.9.3/class/File.html>
| method | mruby-io | memo |
| --------------------------- | -------- | ---- |
@@ -169,25 +169,25 @@ Add the line below to your `build_config.rb`:
| File#size | | |
| File#truncate | | |
-
## License
Copyright (c) 2013 Internet Initiative Japan Inc.
+Copyright (c) 2017 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
+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
+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
+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.
diff --git a/mrbgems/mruby-io/include/mruby/ext/io.h b/mrbgems/mruby-io/include/mruby/ext/io.h
index ba088156e..714f58fd1 100644
--- a/mrbgems/mruby-io/include/mruby/ext/io.h
+++ b/mrbgems/mruby-io/include/mruby/ext/io.h
@@ -5,10 +5,24 @@
#ifndef MRUBY_IO_H
#define MRUBY_IO_H
+#include <mruby.h>
+
+#ifdef MRB_NO_STDIO
+# error IO and File conflicts 'MRB_NO_STDIO' in your build configuration
+#endif
+
#if defined(__cplusplus)
extern "C" {
#endif
+#if defined(MRB_WITHOUT_IO_PREAD_PWRITE)
+# undef MRB_WITH_IO_PREAD_PWRITE
+#elif !defined(MRB_WITH_IO_PREAD_PWRITE)
+# if defined(__unix__) || defined(__MACH__)
+# define MRB_WITH_IO_PREAD_PWRITE
+# endif
+#endif
+
struct mrb_io {
int fd; /* file descriptor, or -1 */
int fd2; /* file descriptor to write if it's different from fd, or -1 */
@@ -19,18 +33,36 @@ struct mrb_io {
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 MRB_O_RDONLY 0x0000
+#define MRB_O_WRONLY 0x0001
+#define MRB_O_RDWR 0x0002
+#define MRB_O_ACCMODE (MRB_O_RDONLY | MRB_O_WRONLY | MRB_O_RDWR)
+#define MRB_O_NONBLOCK 0x0004
+#define MRB_O_APPEND 0x0008
+#define MRB_O_SYNC 0x0010
+#define MRB_O_NOFOLLOW 0x0020
+#define MRB_O_CREAT 0x0040
+#define MRB_O_TRUNC 0x0080
+#define MRB_O_EXCL 0x0100
+#define MRB_O_NOCTTY 0x0200
+#define MRB_O_DIRECT 0x0400
+#define MRB_O_BINARY 0x0800
+#define MRB_O_SHARE_DELETE 0x1000
+#define MRB_O_TMPFILE 0x2000
+#define MRB_O_NOATIME 0x4000
+#define MRB_O_DSYNC 0x00008000
+#define MRB_O_RSYNC 0x00010000
+
+#define MRB_O_RDONLY_P(f) ((mrb_bool)(((f) & MRB_O_ACCMODE) == MRB_O_RDONLY))
+#define MRB_O_WRONLY_P(f) ((mrb_bool)(((f) & MRB_O_ACCMODE) == MRB_O_WRONLY))
+#define MRB_O_RDWR_P(f) ((mrb_bool)(((f) & MRB_O_ACCMODE) == MRB_O_RDWR))
+#define MRB_O_READABLE_P(f) ((mrb_bool)((((f) & MRB_O_ACCMODE) | 2) == 2))
+#define MRB_O_WRITABLE_P(f) ((mrb_bool)(((((f) & MRB_O_ACCMODE) + 1) & 2) == 2))
-#define E_IO_ERROR (mrb_class_get(mrb, "IOError"))
-#define E_EOF_ERROR (mrb_class_get(mrb, "EOFError"))
+#define E_IO_ERROR (mrb_exc_get(mrb, "IOError"))
+#define E_EOF_ERROR (mrb_exc_get(mrb, "EOFError"))
-mrb_value mrb_io_fileno(mrb_state *mrb, mrb_value io);
+int mrb_io_fileno(mrb_state *mrb, mrb_value io);
#if defined(__cplusplus)
} /* extern "C" { */
diff --git a/mrbgems/mruby-io/mrbgem.rake b/mrbgems/mruby-io/mrbgem.rake
index d79964590..b2fc88264 100644
--- a/mrbgems/mruby-io/mrbgem.rake
+++ b/mrbgems/mruby-io/mrbgem.rake
@@ -1,18 +1,12 @@
MRuby::Gem::Specification.new('mruby-io') do |spec|
spec.license = 'MIT'
- spec.authors = 'Internet Initiative Japan Inc.'
+ spec.authors = ['Internet Initiative Japan Inc.', 'mruby developers']
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']
+ if spec.for_windows?
+ spec.linker.libraries << "ws2_32"
end
spec.add_test_dependency 'mruby-time', core: 'mruby-time'
end
diff --git a/mrbgems/mruby-io/mrblib/file.rb b/mrbgems/mruby-io/mrblib/file.rb
index 514efc1c6..9398acef6 100644
--- a/mrbgems/mruby-io/mrblib/file.rb
+++ b/mrbgems/mruby-io/mrblib/file.rb
@@ -1,13 +1,8 @@
class 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
+ if fd_or_path.kind_of? Integer
super(fd_or_path, mode)
else
@path = fd_or_path
@@ -16,6 +11,11 @@ class File < IO
end
end
+ def mtime
+ t = self._mtime
+ t && Time.at(t)
+ end
+
def self.join(*names)
return "" if names.empty?
@@ -60,46 +60,46 @@ class File < IO
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
+ def self._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
- 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("/")
+ unless home_dir
+ raise ArgumentError, "couldn't find HOME environment -- expanding '~'"
+ end
- home_dir = _gethome(user)
+ 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("/")
- unless home_dir
- raise ArgumentError, "user #{user} doesn't exist"
- end
+ home_dir = _gethome(user)
- expanded_path = home_dir
- expanded_path += dir if dir
- expanded_path += "/"
+ unless home_dir
+ raise ArgumentError, "user #{user} doesn't exist"
end
- else
- expanded_path = concat_path(base_path, _getwd)
- expanded_path += "/" + path
- end
- expanded_path
+ 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 = concat_path(path, default_dir)
+ expanded_path
+ end
+
+ def self.expand_path(path, default_dir = '.')
+ 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] == ":"
@@ -191,9 +191,9 @@ class File < IO
def self.extname(filename)
fname = self.basename(filename)
- return '' if fname[0] == '.' || fname.index('.').nil?
- ext = fname.split('.').last
- ext.empty? ? '' : ".#{ext}"
+ epos = fname.rindex('.')
+ return '' if epos == 0 || epos.nil?
+ return fname[epos..-1]
end
def self.path(filename)
diff --git a/mrbgems/mruby-io/mrblib/file_constants.rb b/mrbgems/mruby-io/mrblib/file_constants.rb
index a68ee2598..bd77d53fe 100644
--- a/mrbgems/mruby-io/mrblib/file_constants.rb
+++ b/mrbgems/mruby-io/mrblib/file_constants.rb
@@ -1,21 +1,5 @@
class 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
diff --git a/mrbgems/mruby-io/mrblib/io.rb b/mrbgems/mruby-io/mrblib/io.rb
index 6211bf15f..006578222 100644
--- a/mrbgems/mruby-io/mrblib/io.rb
+++ b/mrbgems/mruby-io/mrblib/io.rb
@@ -123,8 +123,8 @@ class IO
def write(string)
str = string.is_a?(String) ? string : string.to_s
- return str.size unless str.size > 0
- if 0 < @buf.length
+ return 0 if str.empty?
+ unless @buf.empty?
# reset real pos ignore buf
seek(pos, SEEK_SET)
end
@@ -140,8 +140,8 @@ class IO
def eof?
_check_readable
begin
- buf = _read_buf
- return buf.size == 0
+ _read_buf
+ return @buf.empty?
rescue EOFError
return true
end
@@ -150,7 +150,7 @@ class IO
def pos
raise IOError if closed?
- sysseek(0, SEEK_CUR) - @buf.length
+ sysseek(0, SEEK_CUR) - @buf.bytesize
end
alias_method :tell, :pos
@@ -170,23 +170,34 @@ class IO
end
def _read_buf
- return @buf if @buf && @buf.size > 0
- @buf = sysread(BUF_SIZE)
+ return @buf if @buf && @buf.bytesize > 0
+ sysread(BUF_SIZE, @buf)
end
def ungetc(substr)
raise TypeError.new "expect String, got #{substr.class}" unless substr.is_a?(String)
if @buf.empty?
- @buf = substr.dup
+ @buf.replace(substr)
else
- @buf = substr + @buf
+ @buf[0,0] = substr
end
nil
end
+ def ungetbyte(c)
+ if c.is_a? String
+ c = c.getbyte(0)
+ else
+ c &= 0xff
+ end
+ s = " "
+ s.setbyte(0,c)
+ ungetc s
+ end
+
def read(length = nil, outbuf = "")
unless length.nil?
- unless length.is_a? Fixnum
+ unless length.is_a? Integer
raise TypeError.new "can't convert #{length.class} into Integer"
end
if length < 0
@@ -198,7 +209,7 @@ class IO
end
array = []
- while 1
+ while true
begin
_read_buf
rescue EOFError
@@ -207,9 +218,8 @@ class IO
end
if length
- consume = (length <= @buf.size) ? length : @buf.size
- array.push @buf[0, consume]
- @buf = @buf[consume, @buf.size - consume]
+ consume = (length <= @buf.bytesize) ? length : @buf.bytesize
+ array.push IO._bufread(@buf, consume)
length -= consume
break if length == 0
else
@@ -226,12 +236,12 @@ class IO
end
end
- def readline(arg = $/, limit = nil)
+ def readline(arg = "\n", limit = nil)
case arg
when String
rs = arg
- when Fixnum
- rs = $/
+ when Integer
+ rs = "\n"
limit = arg
else
raise ArgumentError
@@ -242,11 +252,11 @@ class IO
end
if rs == ""
- rs = $/ + $/
+ rs = "\n\n"
end
array = []
- while 1
+ while true
begin
_read_buf
rescue EOFError
@@ -256,12 +266,12 @@ class IO
if limit && limit <= @buf.size
array.push @buf[0, limit]
- @buf = @buf[limit, @buf.size - limit]
+ @buf[0, limit] = ""
break
elsif idx = @buf.index(rs)
len = idx + rs.size
array.push @buf[0, len]
- @buf = @buf[len, @buf.size - len]
+ @buf[0, len] = ""
break
else
array.push @buf
@@ -284,9 +294,7 @@ class IO
def readchar
_read_buf
- c = @buf[0]
- @buf = @buf[1, @buf.size]
- c
+ _readchar(@buf)
end
def getc
@@ -297,8 +305,21 @@ class IO
end
end
+ def readbyte
+ _read_buf
+ IO._bufread(@buf, 1).getbyte(0)
+ end
+
+ def getbyte
+ readbyte
+ rescue EOFError
+ nil
+ end
+
# 15.2.20.5.3
def each(&block)
+ return to_enum unless block
+
while line = self.gets
block.call(line)
end
@@ -307,8 +328,10 @@ class IO
# 15.2.20.5.4
def each_byte(&block)
- while char = self.getc
- block.call(char)
+ return to_enum(:each_byte) unless block
+
+ while byte = self.getbyte
+ block.call(byte)
end
self
end
@@ -316,7 +339,14 @@ class IO
# 15.2.20.5.5
alias each_line each
- alias each_char each_byte
+ def each_char(&block)
+ return to_enum(:each_char) unless block
+
+ while char = self.getc
+ block.call(char)
+ end
+ self
+ end
def readlines
ary = []
@@ -330,9 +360,14 @@ class IO
i = 0
len = args.size
while i < len
- s = args[i].to_s
- write s
- write "\n" if (s[-1] != "\n")
+ s = args[i]
+ if s.kind_of?(Array)
+ puts(*s)
+ else
+ s = s.to_s
+ write s
+ write "\n" if (s[-1] != "\n")
+ end
i += 1
end
write "\n" if len == 0
@@ -364,25 +399,3 @@ 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
diff --git a/mrbgems/mruby-io/mrblib/kernel.rb b/mrbgems/mruby-io/mrblib/kernel.rb
index 373b76f98..9cb3b5650 100644
--- a/mrbgems/mruby-io/mrblib/kernel.rb
+++ b/mrbgems/mruby-io/mrblib/kernel.rb
@@ -12,4 +12,20 @@ module Kernel
File.open(file, *rest, &block)
end
end
+
+ 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
end
diff --git a/mrbgems/mruby-io/run_test.rb b/mrbgems/mruby-io/run_test.rb
deleted file mode 100644
index 83d80294a..000000000
--- a/mrbgems/mruby-io/run_test.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/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
diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c
index e65741061..b9e1eca03 100644
--- a/mrbgems/mruby-io/src/file.c
+++ b/mrbgems/mruby-io/src/file.c
@@ -7,12 +7,8 @@
#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 "mruby/presym.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -53,6 +49,7 @@
#if defined(_WIN32) || defined(_WIN64)
#define PATH_SEPARATOR ";"
#define FILE_ALT_SEPARATOR "\\"
+ #define VOLUME_SEPARATOR ":"
#else
#define PATH_SEPARATOR ":"
#endif
@@ -70,7 +67,19 @@
#define LOCK_UN 8
#endif
-#define STAT(p, s) stat(p, s)
+#if !defined(_WIN32) || defined(MRB_MINGW32_LEGACY)
+typedef struct stat mrb_stat;
+# define mrb_stat(path, sb) stat(path, sb)
+# define mrb_fstat(fd, sb) fstat(fd, sb)
+#elif defined MRB_INT32
+typedef struct _stat32 mrb_stat;
+# define mrb_stat(path, sb) _stat32(path, sb)
+# define mrb_fstat(fd, sb) _fstat32(fd, sb)
+#else
+typedef struct _stat64 mrb_stat;
+# define mrb_stat(path, sb) _stat64(path, sb)
+# define mrb_fstat(fd, sb) _fstat64(fd, sb)
+#endif
#ifdef _WIN32
static int
@@ -85,7 +94,7 @@ flock(int fd, int operation) {
}
#endif
-mrb_value
+static mrb_value
mrb_file_s_umask(mrb_state *mrb, mrb_value klass)
{
#if defined(_WIN32) || defined(_WIN64)
@@ -107,7 +116,7 @@ mrb_file_s_umask(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
{
- mrb_value *argv;
+ const mrb_value *argv;
mrb_value pathv;
mrb_int argc, i;
char *path;
@@ -115,8 +124,8 @@ mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
mrb_get_args(mrb, "*", &argv, &argc);
for (i = 0; i < argc; i++) {
const char *utf8_path;
- pathv = mrb_convert_type(mrb, argv[i], MRB_TT_STRING, "String", "to_str");
- utf8_path = mrb_string_value_cstr(mrb, &pathv);
+ pathv = mrb_ensure_string_type(mrb, argv[i]);
+ utf8_path = RSTRING_CSTR(mrb, pathv);
path = mrb_locale_from_utf8(utf8_path, -1);
if (UNLINK(path) < 0) {
mrb_locale_free(path);
@@ -134,8 +143,8 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
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);
+ src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1);
+ dst = mrb_locale_from_utf8(RSTRING_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) {
@@ -146,7 +155,8 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
#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_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "(%v, %v)", from, to)));
+ return mrb_fixnum_value(-1); /* not reached */
}
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -159,12 +169,12 @@ 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];
+ const char *utf8_path;
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);
+ mrb_get_args(mrb, "z", &utf8_path);
+ path = mrb_locale_from_utf8(utf8_path, -1);
+ _splitpath(path, vname, dname, NULL, NULL);
snprintf(buffer, _MAX_DRIVE + _MAX_DIR, "%s%s", vname, dname);
mrb_locale_free(path);
ridx = strlen(buffer);
@@ -248,18 +258,19 @@ mrb_file_realpath(mrb_state *mrb, mrb_value klass)
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);
+ cpath = mrb_locale_from_utf8(RSTRING_CSTR(mrb, pathname), -1);
+ result = mrb_str_new_capa(mrb, PATH_MAX);
if (realpath(cpath, RSTRING_PTR(result)) == NULL) {
mrb_locale_free(cpath);
mrb_sys_fail(mrb, cpath);
+ return result; /* not reached */
}
mrb_locale_free(cpath);
mrb_str_resize(mrb, result, strlen(RSTRING_PTR(result)));
return result;
}
-mrb_value
+static mrb_value
mrb_file__getwd(mrb_state *mrb, mrb_value klass)
{
mrb_value path;
@@ -274,11 +285,58 @@ mrb_file__getwd(mrb_state *mrb, mrb_value klass)
return path;
}
+#ifdef _WIN32
+#define IS_FILESEP(x) (x == (*(char*)(FILE_SEPARATOR)) || x == (*(char*)(FILE_ALT_SEPARATOR)))
+#define IS_VOLSEP(x) (x == (*(char*)(VOLUME_SEPARATOR)))
+#define IS_DEVICEID(x) (x == '.' || x == '?')
+#define CHECK_UNCDEV_PATH (IS_FILESEP(path[0]) && IS_FILESEP(path[1]))
+
+static int
+is_absolute_traditional_path(const char *path, size_t len)
+{
+ if (len < 3) return 0;
+ return (ISALPHA(path[0]) && IS_VOLSEP(path[1]) && IS_FILESEP(path[2]));
+}
+
+static int
+is_absolute_unc_path(const char *path, size_t len) {
+ if (len < 2) return 0;
+ return (CHECK_UNCDEV_PATH && !IS_DEVICEID(path[2]));
+}
+
+static int
+is_absolute_device_path(const char *path, size_t len) {
+ if (len < 4) return 0;
+ return (CHECK_UNCDEV_PATH && IS_DEVICEID(path[2]) && IS_FILESEP(path[3]));
+}
+
+static int
+mrb_file_is_absolute_path(const char *path)
+{
+ size_t len = strlen(path);
+ if (IS_FILESEP(path[0])) return 1;
+ if (len > 0)
+ return (
+ is_absolute_traditional_path(path, len) ||
+ is_absolute_unc_path(path, len) ||
+ is_absolute_device_path(path, len)
+ );
+ else
+ return 0;
+}
+
+#undef IS_FILESEP
+#undef IS_VOLSEP
+#undef IS_DEVICEID
+#undef CHECK_UNCDEV_PATH
+
+#else
static int
mrb_file_is_absolute_path(const char *path)
{
- return (path[0] == '/');
+ return (path[0] == *(char*)(FILE_SEPARATOR));
}
+#endif
static mrb_value
mrb_file__gethome(mrb_state *mrb, mrb_value klass)
@@ -300,21 +358,21 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home");
}
} else {
- const char *cuser = mrb_str_to_cstr(mrb, username);
+ const char *cuser = RSTRING_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);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%v", username);
}
}
home = mrb_locale_from_utf8(home, -1);
path = mrb_str_new_cstr(mrb, home);
mrb_locale_free(home);
return path;
-#else
+#else /* _WIN32 */
argc = mrb_get_argc(mrb);
if (argc == 0) {
home = getenv("USERPROFILE");
@@ -337,18 +395,15 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_file_mtime(mrb_state *mrb, mrb_value self)
{
- mrb_value obj;
- struct stat st;
- int fd;
+ int fd = mrb_io_fileno(mrb, self);
+ mrb_stat st;
- obj = mrb_obj_value(mrb_class_get(mrb, "Time"));
- fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
- if (fstat(fd, &st) == -1)
+ if (mrb_fstat(fd, &st) == -1)
return mrb_false_value();
- return mrb_funcall(mrb, obj, "at", 1, mrb_fixnum_value(st.st_mtime));
+ return mrb_int_value(mrb, (mrb_int)st.st_mtime);
}
-mrb_value
+static mrb_value
mrb_file_flock(mrb_state *mrb, mrb_value self)
{
#if defined(sun)
@@ -358,7 +413,7 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
int fd;
mrb_get_args(mrb, "i", &operation);
- fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
+ fd = mrb_io_fileno(mrb, self);
while (flock(fd, (int)operation) == -1) {
switch (errno) {
@@ -383,6 +438,74 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
}
static mrb_value
+mrb_file_size(mrb_state *mrb, mrb_value self)
+{
+ mrb_stat st;
+ int fd;
+
+ fd = mrb_io_fileno(mrb, self);
+ if (mrb_fstat(fd, &st) == -1) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "fstat failed");
+ }
+
+ if (st.st_size > MRB_INT_MAX) {
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_RUNTIME_ERROR, "File#size too large for MRB_NO_FLOAT");
+#else
+ return mrb_float_value(mrb, (mrb_float)st.st_size);
+#endif
+ }
+
+ return mrb_int_value(mrb, (mrb_int)st.st_size);
+}
+
+static int
+mrb_ftruncate(int fd, mrb_int length)
+{
+#ifndef _WIN32
+ return ftruncate(fd, (off_t)length);
+#else
+ HANDLE file;
+ __int64 cur;
+
+ file = (HANDLE)_get_osfhandle(fd);
+ if (file == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+
+ cur = _lseeki64(fd, 0, SEEK_CUR);
+ if (cur == -1) return -1;
+
+ if (_lseeki64(fd, (__int64)length, SEEK_SET) == -1) return -1;
+
+ if (!SetEndOfFile(file)) {
+ errno = EINVAL; /* TODO: GetLastError to errno */
+ return -1;
+ }
+
+ if (_lseeki64(fd, cur, SEEK_SET) == -1) return -1;
+
+ return 0;
+#endif /* _WIN32 */
+}
+
+static mrb_value
+mrb_file_truncate(mrb_state *mrb, mrb_value self)
+{
+ int fd;
+ mrb_int length;
+ mrb_value lenv = mrb_get_arg1(mrb);
+
+ fd = mrb_io_fileno(mrb, self);
+ length = mrb_as_int(mrb, lenv);
+ if (mrb_ftruncate(fd, length) != 0) {
+ mrb_raise(mrb, E_IO_ERROR, "ftruncate failed");
+ }
+
+ return mrb_fixnum_value(0);
+}
+
+static mrb_value
mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
{
#if defined(_WIN32) || defined(_WIN64)
@@ -393,13 +516,12 @@ mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
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);
-
+ src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1);
+ dst = mrb_locale_from_utf8(RSTRING_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_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "(%v, %v)", from, to)));
}
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -412,21 +534,21 @@ static mrb_value
mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) {
mrb_int mode;
mrb_int argc, i;
- mrb_value *filenames;
+ const 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]);
+ const char *utf8_path = RSTRING_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);
}
- mrb_gc_arena_restore(mrb, ai);
return mrb_fixnum_value(argc);
}
@@ -436,7 +558,8 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) {
mrb_raise(mrb, E_NOTIMP_ERROR, "readlink is not supported on this platform");
return mrb_nil_value(); // unreachable
#else
- char *path, *buf, *tmp;
+ const char *path;
+ char *buf, *tmp;
size_t bufsize = 100;
ssize_t rc;
mrb_value ret;
@@ -470,10 +593,10 @@ mrb_init_file(mrb_state *mrb)
{
struct RClass *io, *file, *cnst;
- io = mrb_class_get(mrb, "IO");
+ io = mrb_class_get_id(mrb, MRB_SYM(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, "umask", mrb_file_s_umask, MRB_ARGS_OPT(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));
@@ -488,20 +611,40 @@ mrb_init_file(mrb_state *mrb)
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));
+ mrb_define_method(mrb, file, "_mtime", mrb_file_mtime, MRB_ARGS_NONE());
+ mrb_define_method(mrb, file, "size", mrb_file_size, MRB_ARGS_NONE());
+ mrb_define_method(mrb, file, "truncate", mrb_file_truncate, MRB_ARGS_REQ(1));
+
+ cnst = mrb_define_module_under_id(mrb, file, MRB_SYM(Constants));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_SH), mrb_fixnum_value(LOCK_SH));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_EX), mrb_fixnum_value(LOCK_EX));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_UN), mrb_fixnum_value(LOCK_UN));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(LOCK_NB), mrb_fixnum_value(LOCK_NB));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(SEPARATOR), mrb_str_new_cstr(mrb, FILE_SEPARATOR));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(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));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(ALT_SEPARATOR), mrb_str_new_cstr(mrb, FILE_ALT_SEPARATOR));
#else
- mrb_define_const(mrb, cnst, "ALT_SEPARATOR", mrb_nil_value());
+ mrb_define_const_id(mrb, cnst, MRB_SYM(ALT_SEPARATOR), mrb_nil_value());
#endif
- mrb_define_const(mrb, cnst, "NULL", mrb_str_new_cstr(mrb, NULL_FILE));
-
+ mrb_define_const_id(mrb, cnst, MRB_SYM(NULL), mrb_str_new_cstr(mrb, NULL_FILE));
+
+ mrb_define_const_id(mrb, cnst, MRB_SYM(RDONLY), mrb_fixnum_value(MRB_O_RDONLY));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(WRONLY), mrb_fixnum_value(MRB_O_WRONLY));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(RDWR), mrb_fixnum_value(MRB_O_RDWR));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(APPEND), mrb_fixnum_value(MRB_O_APPEND));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(CREAT), mrb_fixnum_value(MRB_O_CREAT));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(EXCL), mrb_fixnum_value(MRB_O_EXCL));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(TRUNC), mrb_fixnum_value(MRB_O_TRUNC));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(NONBLOCK), mrb_fixnum_value(MRB_O_NONBLOCK));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(NOCTTY), mrb_fixnum_value(MRB_O_NOCTTY));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(BINARY), mrb_fixnum_value(MRB_O_BINARY));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(SHARE_DELETE), mrb_fixnum_value(MRB_O_SHARE_DELETE));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(SYNC), mrb_fixnum_value(MRB_O_SYNC));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(DSYNC), mrb_fixnum_value(MRB_O_DSYNC));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(RSYNC), mrb_fixnum_value(MRB_O_RSYNC));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(NOFOLLOW), mrb_fixnum_value(MRB_O_NOFOLLOW));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(NOATIME), mrb_fixnum_value(MRB_O_NOATIME));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(DIRECT), mrb_fixnum_value(MRB_O_DIRECT));
+ mrb_define_const_id(mrb, cnst, MRB_SYM(TMPFILE), mrb_fixnum_value(MRB_O_TMPFILE));
}
diff --git a/mrbgems/mruby-io/src/file_test.c b/mrbgems/mruby-io/src/file_test.c
index e429b06b3..f1762369a 100644
--- a/mrbgems/mruby-io/src/file_test.c
+++ b/mrbgems/mruby-io/src/file_test.c
@@ -1,5 +1,5 @@
/*
-** file.c - File class
+** file_test.c - FileTest class
*/
#include "mruby.h"
@@ -7,12 +7,7 @@
#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 <sys/types.h>
#include <sys/stat.h>
@@ -33,7 +28,6 @@
#include <fcntl.h>
#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -42,16 +36,9 @@ 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)) {
+ if (mrb_obj_is_kind_of(mrb, obj, mrb_class_get(mrb, "IO"))) {
struct mrb_io *fptr;
- fptr = (struct mrb_io *)mrb_get_datatype(mrb, obj, &mrb_io_type);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, obj, &mrb_io_type);
if (fptr && fptr->fd >= 0) {
return fstat(fptr->fd, st);
@@ -60,10 +47,8 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
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);
+ else {
+ char *path = mrb_locale_from_utf8(RSTRING_CSTR(mrb, obj), -1);
int ret;
if (do_lstat) {
ret = LSTAT(path, st);
@@ -73,8 +58,6 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
mrb_locale_free(path);
return ret;
}
-
- return -1;
}
static int
@@ -83,11 +66,13 @@ mrb_stat(mrb_state *mrb, mrb_value obj, struct stat *st)
return mrb_stat0(mrb, obj, st, 0);
}
+#ifdef S_ISLNK
static int
mrb_lstat(mrb_state *mrb, mrb_value obj, struct stat *st)
{
return mrb_stat0(mrb, obj, st, 1);
}
+#endif
/*
* Document-method: directory?
@@ -102,7 +87,7 @@ mrb_lstat(mrb_state *mrb, mrb_value obj, struct stat *st)
* File.directory?(".")
*/
-mrb_value
+static mrb_value
mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
{
#ifndef S_ISDIR
@@ -110,9 +95,7 @@ mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
#endif
struct stat st;
- mrb_value obj;
-
- mrb_get_args(mrb, "o", &obj);
+ mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
@@ -129,7 +112,7 @@ mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
* Returns <code>true</code> if the named file is a pipe.
*/
-mrb_value
+static mrb_value
mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
{
#if defined(_WIN32) || defined(_WIN64)
@@ -141,9 +124,7 @@ mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
# endif
struct stat st;
- mrb_value obj;
-
- mrb_get_args(mrb, "o", &obj);
+ mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
@@ -162,7 +143,7 @@ mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
* Returns <code>true</code> if the named file is a symbolic link.
*/
-mrb_value
+static mrb_value
mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
{
#if defined(_WIN32) || defined(_WIN64)
@@ -184,9 +165,7 @@ mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
#ifdef S_ISLNK
struct stat st;
- mrb_value obj;
-
- mrb_get_args(mrb, "o", &obj);
+ mrb_value obj = mrb_get_arg1(mrb);
if (mrb_lstat(mrb, obj, &st) == -1)
return mrb_false_value();
@@ -205,7 +184,7 @@ mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
* Returns <code>true</code> if the named file is a socket.
*/
-mrb_value
+static mrb_value
mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
{
#if defined(_WIN32) || defined(_WIN64)
@@ -227,9 +206,7 @@ mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
#ifdef S_ISSOCK
struct stat st;
- mrb_value obj;
-
- mrb_get_args(mrb, "o", &obj);
+ mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
@@ -249,13 +226,12 @@ mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
* Return <code>true</code> if the named file exists.
*/
-mrb_value
+static mrb_value
mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass)
{
struct stat st;
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &obj);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
@@ -270,7 +246,7 @@ mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass)
* regular file.
*/
-mrb_value
+static mrb_value
mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
{
#ifndef S_ISREG
@@ -278,9 +254,7 @@ mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
#endif
struct stat st;
- mrb_value obj;
-
- mrb_get_args(mrb, "o", &obj);
+ mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
@@ -298,13 +272,11 @@ mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
* a zero size.
*/
-mrb_value
+static 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);
+ mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
return mrb_false_value();
@@ -323,18 +295,16 @@ mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass)
* _file_name_ can be an IO object.
*/
-mrb_value
+static mrb_value
mrb_filetest_s_size(mrb_state *mrb, mrb_value klass)
{
struct stat st;
- mrb_value obj;
-
- mrb_get_args(mrb, "o", &obj);
+ mrb_value obj = mrb_get_arg1(mrb);
if (mrb_stat(mrb, obj, &st) < 0)
mrb_sys_fail(mrb, "mrb_stat");
- return mrb_fixnum_value(st.st_size);
+ return mrb_int_value(mrb, st.st_size);
}
/*
@@ -345,20 +315,18 @@ mrb_filetest_s_size(mrb_state *mrb, mrb_value klass)
* file otherwise.
*/
-mrb_value
+static 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);
+ mrb_value obj = mrb_get_arg1(mrb);
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);
+ return mrb_int_value(mrb, st.st_size);
}
void
diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c
index 1ac15aab3..0c79660a8 100644
--- a/mrbgems/mruby-io/src/io.c
+++ b/mrbgems/mruby-io/src/io.c
@@ -10,12 +10,8 @@
#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 "mruby/presym.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -23,6 +19,7 @@
#if defined(_WIN32) || defined(_WIN64)
#include <winsock.h>
#include <io.h>
+ #include <basetsd.h>
#define open _open
#define close _close
#define dup _dup
@@ -36,14 +33,27 @@
typedef long ftime_t;
typedef long fsuseconds_t;
typedef int fmode_t;
+ typedef int mrb_io_read_write_size;
+ #ifndef MRB_MINGW32_LEGACY
+ #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) && \
+ !defined(__have_typedef_ssize_t)
+ typedef SSIZE_T ssize_t;
+ #endif
+ #endif
+
+ #ifndef O_TMPFILE
+ #define O_TMPFILE O_TEMPORARY
+ #endif
#else
#include <sys/wait.h>
+ #include <sys/time.h>
#include <unistd.h>
typedef size_t fsize_t;
typedef time_t ftime_t;
typedef suseconds_t fsuseconds_t;
typedef mode_t fmode_t;
+ typedef ssize_t mrb_io_read_write_size;
#endif
#ifdef _MSC_VER
@@ -53,9 +63,14 @@ typedef mrb_int pid_t;
#include <fcntl.h>
#include <errno.h>
-#include <stdio.h>
#include <string.h>
+#define OPEN_ACCESS_MODE_FLAGS (O_RDONLY | O_WRONLY | O_RDWR)
+#define OPEN_RDONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDONLY))
+#define OPEN_WRONLY_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_WRONLY))
+#define OPEN_RDWR_P(f) ((mrb_bool)(((f) & OPEN_ACCESS_MODE_FLAGS) == O_RDWR))
+#define OPEN_READABLE_P(f) ((mrb_bool)(OPEN_RDONLY_P(f) || OPEN_RDWR_P(f)))
+#define OPEN_WRITABLE_P(f) ((mrb_bool)(OPEN_WRONLY_P(f) || OPEN_RDWR_P(f)))
static void mrb_io_free(mrb_state *mrb, void *ptr);
struct mrb_data_type mrb_io_type = { "IO", mrb_io_free };
@@ -63,28 +78,20 @@ 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 int mrb_io_mode_to_flags(mrb_state *mrb, mrb_value mode);
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);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, self, &mrb_io_type);
if (fptr == NULL) {
- mrb_raise(mrb, E_IO_ERROR, "uninitialized stream.");
+ mrb_raise(mrb, E_IO_ERROR, "uninitialized stream");
}
if (fptr->fd < 0) {
- mrb_raise(mrb, E_IO_ERROR, "closed stream.");
+ mrb_raise(mrb, E_IO_ERROR, "closed stream");
}
return fptr;
}
@@ -96,52 +103,55 @@ io_set_process_status(mrb_state *mrb, pid_t pid, int 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 (mrb_class_defined_id(mrb, MRB_SYM(Process))) {
+ c_process = mrb_module_get_id(mrb, MRB_SYM(Process));
+ if (mrb_const_defined(mrb, mrb_obj_value(c_process), MRB_SYM(Status))) {
+ c_status = mrb_class_get_under_id(mrb, c_process, MRB_SYM(Status));
}
}
if (c_status != NULL) {
- v = mrb_funcall(mrb, mrb_obj_value(c_status), "new", 2, mrb_fixnum_value(pid), mrb_fixnum_value(status));
+ v = mrb_funcall_id(mrb, mrb_obj_value(c_status), MRB_SYM(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);
+ mrb_gv_set(mrb, mrb_intern_lit(mrb, "$?"), v);
}
static int
mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
{
- int flags = 0;
+ int flags;
const char *m = mode;
switch (*m++) {
case 'r':
- flags |= FMODE_READABLE;
+ flags = O_RDONLY;
break;
case 'w':
- flags |= FMODE_WRITABLE | FMODE_CREATE | FMODE_TRUNC;
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
break;
case 'a':
- flags |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
+ flags = O_WRONLY | O_CREAT | O_APPEND;
break;
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
+ flags = 0; /* not reached */
}
while (*m) {
switch (*m++) {
case 'b':
- flags |= FMODE_BINMODE;
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
break;
case '+':
- flags |= FMODE_READWRITE;
+ flags = (flags & ~OPEN_ACCESS_MODE_FLAGS) | O_RDWR;
break;
case ':':
/* XXX: PASSTHROUGH*/
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
}
}
@@ -149,38 +159,72 @@ mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
}
static int
-mrb_io_flags_to_modenum(mrb_state *mrb, int flags)
+mrb_io_mode_to_flags(mrb_state *mrb, mrb_value mode)
{
- 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 (mrb_nil_p(mode)) {
+ return mrb_io_modestr_to_flags(mrb, "r");
}
- if (flags & FMODE_CREATE) {
- modenum |= O_CREAT;
+ else if (mrb_string_p(mode)) {
+ return mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
}
+ else {
+ int flags = 0;
+ mrb_int flags0 = mrb_as_int(mrb, mode);
+
+ switch (flags0 & MRB_O_ACCMODE) {
+ case MRB_O_RDONLY:
+ flags |= O_RDONLY;
+ break;
+ case MRB_O_WRONLY:
+ flags |= O_WRONLY;
+ break;
+ case MRB_O_RDWR:
+ flags |= O_RDWR;
+ break;
+ default:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %v", mode);
+ }
+
+ if (flags0 & MRB_O_APPEND) flags |= O_APPEND;
+ if (flags0 & MRB_O_CREAT) flags |= O_CREAT;
+ if (flags0 & MRB_O_EXCL) flags |= O_EXCL;
+ if (flags0 & MRB_O_TRUNC) flags |= O_TRUNC;
+#ifdef O_NONBLOCK
+ if (flags0 & MRB_O_NONBLOCK) flags |= O_NONBLOCK;
+#endif
+#ifdef O_NOCTTY
+ if (flags0 & MRB_O_NOCTTY) flags |= O_NOCTTY;
+#endif
#ifdef O_BINARY
- if (flags & FMODE_BINMODE) {
- modenum |= O_BINARY;
- }
+ if (flags0 & MRB_O_BINARY) flags |= O_BINARY;
+#endif
+#ifdef O_SHARE_DELETE
+ if (flags0 & MRB_O_SHARE_DELETE) flags |= O_SHARE_DELETE;
+#endif
+#ifdef O_SYNC
+ if (flags0 & MRB_O_SYNC) flags |= O_SYNC;
+#endif
+#ifdef O_DSYNC
+ if (flags0 & MRB_O_DSYNC) flags |= O_DSYNC;
+#endif
+#ifdef O_RSYNC
+ if (flags0 & MRB_O_RSYNC) flags |= O_RSYNC;
+#endif
+#ifdef O_NOFOLLOW
+ if (flags0 & MRB_O_NOFOLLOW) flags |= O_NOFOLLOW;
+#endif
+#ifdef O_NOATIME
+ if (flags0 & MRB_O_NOATIME) flags |= O_NOATIME;
+#endif
+#ifdef O_DIRECT
+ if (flags0 & MRB_O_DIRECT) flags |= O_DIRECT;
+#endif
+#ifdef O_TMPFILE
+ if (flags0 & MRB_O_TMPFILE) flags |= O_TMPFILE;
#endif
- return modenum;
+ return flags;
+ }
}
static void
@@ -191,8 +235,7 @@ mrb_fd_cloexec(mrb_state *mrb, int fd)
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));
+ mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%d, F_GETFD) failed: %d", fd, errno);
}
if (fd <= 2) {
flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */
@@ -202,14 +245,13 @@ mrb_fd_cloexec(mrb_state *mrb, int fd)
}
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));
+ mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%d, F_SETFD, %d) failed: %d", fd, flags2, errno);
}
}
#endif
}
-#if !defined(_WIN32) && !TARGET_OS_IPHONE
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
static int
mrb_cloexec_pipe(mrb_state *mrb, int fildes[2])
{
@@ -285,19 +327,20 @@ mrb_io_alloc(mrb_state *mrb)
#define NOFILE 64
#endif
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+# define mrb_io_s_popen mrb_notimplement_m
+#else
static int
-option_to_fd(mrb_state *mrb, mrb_value obj, const char *key)
+option_to_fd(mrb_state *mrb, mrb_value v)
{
- 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;
- }
+ if (mrb_undef_p(v)) return -1;
+ if (mrb_nil_p(v)) return -1;
- switch (mrb_type(opt)) {
+ switch (mrb_type(v)) {
case MRB_TT_DATA: /* IO */
- return (int)mrb_fixnum(mrb_io_fileno(mrb, opt));
- case MRB_TT_FIXNUM:
- return (int)mrb_fixnum(opt);
+ return mrb_io_fileno(mrb, v);
+ case MRB_TT_INTEGER:
+ return (int)mrb_integer(v);
default:
mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong exec redirect action");
break;
@@ -305,16 +348,42 @@ option_to_fd(mrb_state *mrb, mrb_value obj, const char *key)
return -1; /* never reached */
}
+static mrb_value
+mrb_io_s_popen_args(mrb_state *mrb, mrb_value klass,
+ const char **cmd, int *flags, int *doexec,
+ int *opt_in, int *opt_out, int *opt_err)
+{
+ mrb_value mode = mrb_nil_value();
+ struct { mrb_value opt_in, opt_out, opt_err; } kv;
+ mrb_sym knames[3] = {MRB_SYM(in), MRB_SYM(out), MRB_SYM(err)};
+ const mrb_kwargs kw = {
+ 3, 0,
+ knames,
+ &kv.opt_in,
+ NULL,
+ };
+
+ mrb_get_args(mrb, "z|o:", cmd, &mode, &kw);
+
+ *flags = mrb_io_mode_to_flags(mrb, mode);
+ *doexec = (strcmp("-", *cmd) != 0);
+ *opt_in = option_to_fd(mrb, kv.opt_in);
+ *opt_out = option_to_fd(mrb, kv.opt_out);
+ *opt_err = option_to_fd(mrb, kv.opt_err);
+
+ return mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+}
+
#ifdef _WIN32
-mrb_value
+static 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);
+ mrb_value io;
+ int doexec;
+ int opt_in, opt_out, opt_err;
+ const char *cmd;
struct mrb_io *fptr;
- const char *pname;
int pid = 0, flags;
STARTUPINFO si;
PROCESS_INFORMATION pi;
@@ -323,37 +392,26 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
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");
+ io = mrb_io_s_popen_args(mrb, klass, &cmd, &flags, &doexec,
+ &opt_in, &opt_out, &opt_err);
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
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 (OPEN_WRITABLE_P(flags)) {
if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
|| !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
mrb_sys_fail(mrb, "pipe");
@@ -367,21 +425,21 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.dwFlags |= STARTF_USESTDHANDLES;
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
si.hStdOutput = ofd[1];
si.hStdError = ofd[1];
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
si.hStdInput = ifd[0];
}
if (!CreateProcess(
- NULL, (char*)pname, NULL, NULL,
+ NULL, (char*)cmd, 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);
+ mrb_raisef(mrb, E_IO_ERROR, "command not found: %s", cmd);
}
CloseHandle(pi.hThread);
CloseHandle(ifd[0]);
@@ -389,56 +447,39 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
pid = pi.dwProcessId;
}
- mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+ mrb_iv_set(mrb, io, mrb_intern_lit(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->readable = OPEN_READABLE_P(flags);
+ fptr->writable = OPEN_WRITABLE_P(flags);
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
+static 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);
+ mrb_value io, result;
+ int doexec;
+ int opt_in, opt_out, opt_err;
+ const char *cmd;
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));
+ io = mrb_io_s_popen_args(mrb, klass, &cmd, &flags, &doexec,
+ &opt_in, &opt_out, &opt_err);
- 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 (OPEN_READABLE_P(flags)) {
if (pipe(pr) == -1) {
mrb_sys_fail(mrb, "pipe");
}
@@ -446,7 +487,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
mrb_fd_cloexec(mrb, pr[1]);
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
if (pipe(pw) == -1) {
if (pr[0] != -1) close(pr[0]);
if (pr[1] != -1) close(pr[1]);
@@ -475,14 +516,14 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
if (opt_err != -1) {
dup2(opt_err, 2);
}
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
close(pr[0]);
if (pr[1] != 1) {
dup2(pr[1], 1);
close(pr[1]);
}
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
close(pw[1]);
if (pw[0] != 0) {
dup2(pw[0], 0);
@@ -493,20 +534,20 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
for (fd = 3; fd < NOFILE; fd++) {
close(fd);
}
- mrb_proc_exec(pname);
- mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+ mrb_proc_exec(cmd);
+ 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)) {
+ if (OPEN_RDWR_P(flags)) {
close(pr[1]);
fd = pr[0];
close(pw[0]);
write_fd = pw[1];
- } else if (flags & FMODE_READABLE) {
+ } else if (OPEN_RDONLY_P(flags)) {
close(pr[1]);
fd = pr[0];
} else {
@@ -514,14 +555,14 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
fd = pw[1];
}
- mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+ mrb_iv_set(mrb, io, mrb_intern_lit(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->readable = OPEN_READABLE_P(flags);
+ fptr->writable = OPEN_WRITABLE_P(flags);
fptr->sync = 0;
DATA_TYPE(io) = &mrb_io_type;
@@ -531,21 +572,22 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
case -1: /* error */
saved_errno = errno;
- if (flags & FMODE_READABLE) {
+ if (OPEN_READABLE_P(flags)) {
close(pr[0]);
close(pr[1]);
}
- if (flags & FMODE_WRITABLE) {
+ if (OPEN_WRITABLE_P(flags)) {
close(pw[0]);
close(pw[1]);
}
errno = saved_errno;
- mrb_sys_fail(mrb, "pipe_open failed.");
+ mrb_sys_fail(mrb, "pipe_open failed");
break;
}
return result;
}
-#endif
+#endif /* _WIN32 */
+#endif /* TARGET_OS_IPHONE */
static int
mrb_dup(mrb_state *mrb, int fd, mrb_bool *failed)
@@ -561,16 +603,15 @@ mrb_dup(mrb_state *mrb, int fd, mrb_bool *failed)
return new_fd;
}
-mrb_value
+static mrb_value
mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy)
{
- mrb_value orig;
+ mrb_value orig = mrb_get_arg1(mrb);
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;
@@ -583,8 +624,8 @@ mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy)
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);
+ buf = mrb_iv_get(mrb, orig, mrb_intern_lit(mrb, "@buf"));
+ mrb_iv_set(mrb, copy, mrb_intern_lit(mrb, "@buf"), buf);
fptr_copy->fd = mrb_dup(mrb, fptr_orig->fd, &failed);
if (failed) {
@@ -610,7 +651,44 @@ mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy)
return copy;
}
-mrb_value
+static void
+check_file_descriptor(mrb_state *mrb, mrb_int fd)
+{
+ struct stat sb;
+ int fdi = (int)fd;
+
+#if MRB_INT_MIN < INT_MIN || MRB_INT_MAX > INT_MAX
+ if (fdi != fd) {
+ goto badfd;
+ }
+#endif
+
+#ifdef _WIN32
+ {
+ DWORD err;
+ int len = sizeof(err);
+
+ if (getsockopt(fdi, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) {
+ return;
+ }
+ }
+
+ if (fdi < 0 || fdi > _getmaxstdio()) {
+ goto badfd;
+ }
+#endif /* _WIN32 */
+
+ if (fstat(fdi, &sb) != 0) {
+ goto badfd;
+ }
+
+ return;
+
+badfd:
+ mrb_sys_fail(mrb, "bad file descriptor");
+}
+
+static mrb_value
mrb_io_initialize(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
@@ -620,7 +698,16 @@ mrb_io_initialize(mrb_state *mrb, mrb_value io)
mode = opt = mrb_nil_value();
- mrb_get_args(mrb, "i|So", &fd, &mode, &opt);
+ mrb_get_args(mrb, "i|oo", &fd, &mode, &opt);
+ switch (fd) {
+ case 0: /* STDIN_FILENO */
+ case 1: /* STDOUT_FILENO */
+ case 2: /* STDERR_FILENO */
+ break;
+ default:
+ check_file_descriptor(mrb, fd);
+ break;
+ }
if (mrb_nil_p(mode)) {
mode = mrb_str_new_cstr(mrb, "r");
}
@@ -628,9 +715,9 @@ mrb_io_initialize(mrb_state *mrb, mrb_value io)
opt = mrb_hash_new(mrb);
}
- flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+ flags = mrb_io_mode_to_flags(mrb, mode);
- mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+ mrb_iv_set(mrb, io, mrb_intern_lit(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
fptr = (struct mrb_io *)DATA_PTR(io);
if (fptr != NULL) {
@@ -643,8 +730,8 @@ mrb_io_initialize(mrb_state *mrb, mrb_value io)
DATA_PTR(io) = fptr;
fptr->fd = (int)fd;
- fptr->readable = ((flags & FMODE_READABLE) != 0);
- fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+ fptr->readable = OPEN_READABLE_P(flags);
+ fptr->writable = OPEN_WRITABLE_P(flags);
fptr->sync = 0;
return io;
}
@@ -708,11 +795,11 @@ fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet)
if (!quiet && saved_errno != 0) {
errno = saved_errno;
- mrb_sys_fail(mrb, "fptr_finalize failed.");
+ mrb_sys_fail(mrb, "fptr_finalize failed");
}
}
-mrb_value
+static mrb_value
mrb_io_check_readable(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr = io_get_open_fptr(mrb, self);
@@ -722,7 +809,7 @@ mrb_io_check_readable(mrb_state *mrb, mrb_value self)
return mrb_nil_value();
}
-mrb_value
+static mrb_value
mrb_io_isatty(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr;
@@ -733,7 +820,7 @@ mrb_io_isatty(mrb_state *mrb, mrb_value self)
return mrb_true_value();
}
-mrb_value
+static mrb_value
mrb_io_s_for_fd(mrb_state *mrb, mrb_value klass)
{
struct RClass *c = mrb_class_ptr(klass);
@@ -746,7 +833,7 @@ mrb_io_s_for_fd(mrb_state *mrb, mrb_value klass)
return mrb_io_initialize(mrb, obj);
}
-mrb_value
+static mrb_value
mrb_io_s_sysclose(mrb_state *mrb, mrb_value klass)
{
mrb_int fd;
@@ -757,10 +844,9 @@ mrb_io_s_sysclose(mrb_state *mrb, mrb_value klass)
return mrb_fixnum_value(0);
}
-int
+static 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);
@@ -783,9 +869,7 @@ 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_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "open %s", pathname)));
}
mrb_locale_free(fname);
@@ -795,39 +879,55 @@ reopen:
return fd;
}
-mrb_value
+static 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;
+ int flags;
- mrb_get_args(mrb, "S|Si", &path, &mode, &perm);
- if (mrb_nil_p(mode)) {
- mode = mrb_str_new_cstr(mrb, "r");
- }
+ mrb_get_args(mrb, "S|oi", &path, &mode, &perm);
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);
+ pat = RSTRING_CSTR(mrb, path);
+ flags = mrb_io_mode_to_flags(mrb, mode);
+ fd = mrb_cloexec_open(mrb, pat, flags, perm);
return mrb_fixnum_value(fd);
}
-mrb_value
+static mrb_value mrb_io_sysread_common(mrb_state *mrb,
+ mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
+ mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset);
+
+static mrb_io_read_write_size
+mrb_sysread_dummy(int fd, void *buf, fsize_t nbytes, off_t offset)
+{
+ return (mrb_io_read_write_size)read(fd, buf, nbytes);
+}
+
+static 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);
+
+ return mrb_io_sysread_common(mrb, mrb_sysread_dummy, io, buf, maxlen, 0);
+}
+
+static mrb_value
+mrb_io_sysread_common(mrb_state *mrb,
+ mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
+ mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset)
+{
+ struct mrb_io *fptr;
+ int ret;
+
if (maxlen < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size");
}
@@ -841,7 +941,8 @@ mrb_io_sysread(mrb_state *mrb, mrb_value io)
if (RSTRING_LEN(buf) != maxlen) {
buf = mrb_str_resize(mrb, buf, maxlen);
- } else {
+ }
+ else {
mrb_str_modify(mrb, RSTRING(buf));
}
@@ -849,29 +950,20 @@ mrb_io_sysread(mrb_state *mrb, mrb_value 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;
+ ret = readfunc(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen, offset);
+ if (ret < 0) {
+ mrb_sys_fail(mrb, "sysread failed");
+ }
+ if (RSTRING_LEN(buf) != ret) {
+ buf = mrb_str_resize(mrb, buf, ret);
+ }
+ if (ret == 0 && maxlen > 0) {
+ mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File");
}
-
return buf;
}
-mrb_value
+static mrb_value
mrb_io_sysseek(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
@@ -889,21 +981,22 @@ mrb_io_sysseek(mrb_state *mrb, mrb_value io)
mrb_sys_fail(mrb, "sysseek");
}
if (pos > MRB_INT_MAX) {
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
return mrb_float_value(mrb, (mrb_float)pos);
#else
- mrb_raise(mrb, E_IO_ERROR, "sysseek reached too far for MRB_WITHOUT_FLOAT");
+ mrb_raise(mrb, E_IO_ERROR, "sysseek reached too far for MRB_NO_FLOAT");
#endif
} else {
- return mrb_fixnum_value(pos);
+ return mrb_int_value(mrb, pos);
}
}
-mrb_value
-mrb_io_syswrite(mrb_state *mrb, mrb_value io)
+static mrb_value
+mrb_io_syswrite_common(mrb_state *mrb,
+ mrb_io_read_write_size (*writefunc)(int, const void *, fsize_t, off_t),
+ mrb_value io, mrb_value buf, off_t offset)
{
struct mrb_io *fptr;
- mrb_value str, buf;
int fd, length;
fptr = io_get_open_fptr(mrb, io);
@@ -911,27 +1004,36 @@ mrb_io_syswrite(mrb_state *mrb, mrb_value io)
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));
+ length = writefunc(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf), offset);
if (length == -1) {
mrb_sys_fail(mrb, 0);
}
- return mrb_fixnum_value(length);
+ return mrb_int_value(mrb, (mrb_int)length);
+}
+
+static mrb_io_read_write_size
+mrb_syswrite_dummy(int fd, const void *buf, fsize_t nbytes, off_t offset)
+{
+ return (mrb_io_read_write_size)write(fd, buf, nbytes);
+}
+
+static mrb_value
+mrb_io_syswrite(mrb_state *mrb, mrb_value io)
+{
+ mrb_value buf;
+
+ mrb_get_args(mrb, "S", &buf);
+
+ return mrb_io_syswrite_common(mrb, mrb_syswrite_dummy, io, buf, 0);
}
-mrb_value
+static mrb_value
mrb_io_close(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr;
@@ -940,7 +1042,7 @@ mrb_io_close(mrb_state *mrb, mrb_value self)
return mrb_nil_value();
}
-mrb_value
+static mrb_value
mrb_io_close_write(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr;
@@ -951,11 +1053,11 @@ mrb_io_close_write(mrb_state *mrb, mrb_value self)
return mrb_nil_value();
}
-mrb_value
+static 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);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, io, &mrb_io_type);
if (fptr == NULL || fptr->fd >= 0) {
return mrb_false_value();
}
@@ -963,7 +1065,7 @@ mrb_io_closed(mrb_state *mrb, mrb_value io)
return mrb_true_value();
}
-mrb_value
+static mrb_value
mrb_io_pid(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
@@ -982,12 +1084,12 @@ 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);
+ case MRB_TT_INTEGER:
+ t.tv_sec = (ftime_t)mrb_integer(time);
t.tv_usec = 0;
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_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);
@@ -1004,14 +1106,14 @@ time2timeval(mrb_state *mrb, mrb_value time)
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) {
+ mrb_value buf = mrb_iv_get(mrb, io, mrb_intern_lit(mrb, "@buf"));
+ if (mrb_string_p(buf) && RSTRING_LEN(buf) > 0) {
return 1;
}
return 0;
}
-#if !defined(_WIN32) && !TARGET_OS_IPHONE
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
static mrb_value
mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
{
@@ -1026,7 +1128,7 @@ mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
}
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, ""));
+ mrb_iv_set(mrb, r, mrb_intern_lit(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
fptr_r = mrb_io_alloc(mrb);
fptr_r->fd = pipes[0];
fptr_r->readable = 1;
@@ -1036,7 +1138,7 @@ mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
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, ""));
+ mrb_iv_set(mrb, w, mrb_intern_lit(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
fptr_w = mrb_io_alloc(mrb);
fptr_w->fd = pipes[1];
fptr_w->readable = 0;
@@ -1052,7 +1154,7 @@ mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_io_s_select(mrb_state *mrb, mrb_value klass)
{
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc;
mrb_value read, read_io, write, except, timeout, list;
struct timeval *tp, timerec;
@@ -1068,7 +1170,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
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));
+ mrb_argnum_error(mrb, argc, 1, 4);
}
timeout = mrb_nil_value();
@@ -1097,6 +1199,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
for (i = 0; i < RARRAY_LEN(read); i++) {
read_io = RARRAY_PTR(read)[i];
fptr = io_get_open_fptr(mrb, read_io);
+ if (fptr->fd >= FD_SETSIZE) continue;
FD_SET(fptr->fd, rp);
if (mrb_io_read_data_pending(mrb, read_io)) {
pending++;
@@ -1119,6 +1222,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
FD_ZERO(wp);
for (i = 0; i < RARRAY_LEN(write); i++) {
fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
+ if (fptr->fd >= FD_SETSIZE) continue;
FD_SET(fptr->fd, wp);
if (max < fptr->fd)
max = fptr->fd;
@@ -1138,6 +1242,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
FD_ZERO(ep);
for (i = 0; i < RARRAY_LEN(except); i++) {
fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
+ if (fptr->fd >= FD_SETSIZE) continue;
FD_SET(fptr->fd, ep);
if (max < fptr->fd)
max = fptr->fd;
@@ -1211,18 +1316,25 @@ retry:
return result;
}
-mrb_value
+int
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);
+ return fptr->fd;
}
-mrb_value
-mrb_io_close_on_exec_p(mrb_state *mrb, mrb_value self)
+static mrb_value
+mrb_io_fileno_m(mrb_state *mrb, mrb_value io)
{
+ int fd = mrb_io_fileno(mrb, io);
+ return mrb_fixnum_value(fd);
+}
+
#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
+static mrb_value
+mrb_io_close_on_exec_p(mrb_state *mrb, mrb_value self)
+{
struct mrb_io *fptr;
int ret;
@@ -1236,17 +1348,15 @@ mrb_io_close_on_exec_p(mrb_state *mrb, mrb_value self)
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();
+# define mrb_io_close_on_exec_p mrb_notimplement_m
#endif
-}
-mrb_value
+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
+static 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;
@@ -1273,13 +1383,12 @@ mrb_io_set_close_on_exec(mrb_state *mrb, mrb_value self)
}
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();
+# define mrb_io_set_close_on_exec mrb_notimplement_m
#endif
-}
-mrb_value
+static mrb_value
mrb_io_set_sync(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr;
@@ -1291,7 +1400,7 @@ mrb_io_set_sync(mrb_state *mrb, mrb_value self)
return mrb_bool_value(b);
}
-mrb_value
+static mrb_value
mrb_io_sync(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr;
@@ -1299,6 +1408,118 @@ mrb_io_sync(mrb_state *mrb, mrb_value self)
return mrb_bool_value(fptr->sync);
}
+#ifndef MRB_WITH_IO_PREAD_PWRITE
+# define mrb_io_pread mrb_notimplement_m
+# define mrb_io_pwrite mrb_notimplement_m
+#else
+static off_t
+value2off(mrb_state *mrb, mrb_value offv)
+{
+ return (off_t)mrb_as_int(mrb, offv);
+}
+
+/*
+ * call-seq:
+ * pread(maxlen, offset, outbuf = "") -> outbuf
+ */
+static mrb_value
+mrb_io_pread(mrb_state *mrb, mrb_value io)
+{
+ mrb_value buf = mrb_nil_value();
+ mrb_value off;
+ mrb_int maxlen;
+
+ mrb_get_args(mrb, "io|S!", &maxlen, &off, &buf);
+
+ return mrb_io_sysread_common(mrb, pread, io, buf, maxlen, value2off(mrb, off));
+}
+
+/*
+ * call-seq:
+ * pwrite(buffer, offset) -> wrote_bytes
+ */
+static mrb_value
+mrb_io_pwrite(mrb_state *mrb, mrb_value io)
+{
+ mrb_value buf, off;
+
+ mrb_get_args(mrb, "So", &buf, &off);
+
+ return mrb_io_syswrite_common(mrb, pwrite, io, buf, value2off(mrb, off));
+}
+#endif /* MRB_WITH_IO_PREAD_PWRITE */
+
+static mrb_value
+io_bufread(mrb_state *mrb, mrb_value str, mrb_int len)
+{
+ mrb_value str2;
+ mrb_int newlen;
+ struct RString *s;
+ char *p;
+
+ s = RSTRING(str);
+ mrb_str_modify(mrb, s);
+ p = RSTR_PTR(s);
+ str2 = mrb_str_new(mrb, p, len);
+ newlen = RSTR_LEN(s)-len;
+ memmove(p, p+len, newlen);
+ p[newlen] = '\0';
+ RSTR_SET_LEN(s, newlen);
+
+ return str2;
+}
+
+static mrb_value
+mrb_io_bufread(mrb_state *mrb, mrb_value self)
+{
+ mrb_value str;
+ mrb_int len;
+
+ mrb_get_args(mrb, "Si", &str, &len);
+ mrb_assert(RSTRING_LEN(str) > 0);
+ mrb_assert(RSTRING_PTR(str) != NULL);
+ mrb_str_modify(mrb, RSTRING(str));
+ return io_bufread(mrb, str, len);
+}
+
+static mrb_value
+mrb_io_readchar(mrb_state *mrb, mrb_value self)
+{
+ mrb_value buf;
+ mrb_int len = 1;
+#ifdef MRB_UTF8_STRING
+ unsigned char c;
+#endif
+
+ mrb_get_args(mrb, "S", &buf);
+ mrb_assert(RSTRING_LEN(buf) > 0);
+ mrb_assert(RSTRING_PTR(buf) != NULL);
+ mrb_str_modify(mrb, RSTRING(buf));
+#ifdef MRB_UTF8_STRING
+ c = RSTRING_PTR(buf)[0];
+ if (c & 0x80) {
+ len = mrb_utf8len(RSTRING_PTR(buf), RSTRING_END(buf));
+ if (len == 1 && RSTRING_LEN(buf) < 4) { /* partial UTF-8 */
+ mrb_int blen = RSTRING_LEN(buf);
+ ssize_t n;
+
+ struct mrb_io *fptr = (struct mrb_io*)io_get_open_fptr(mrb, self);
+
+ if (!fptr->readable) {
+ mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
+ }
+ /* refill the buffer */
+ mrb_str_resize(mrb, buf, 4096);
+ n = read(fptr->fd, RSTRING_PTR(buf)+blen, 4096-blen);
+ if (n < 0) mrb_sys_fail(mrb, "sysread failed");
+ mrb_str_resize(mrb, buf, blen+n);
+ }
+ len = mrb_utf8len(RSTRING_PTR(buf), RSTRING_END(buf));
+ }
+#endif
+ return io_bufread(mrb, buf, len);
+}
+
void
mrb_init_io(mrb_state *mrb)
{
@@ -1308,23 +1529,23 @@ mrb_init_io(mrb_state *mrb)
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, "_popen", mrb_io_s_popen, MRB_ARGS_ARG(1,2));
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, "for_fd", mrb_io_s_for_fd, MRB_ARGS_ARG(1,2));
+ mrb_define_class_method(mrb, io, "select", mrb_io_s_select, MRB_ARGS_ARG(1,3));
+ mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ARG(1,2));
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && 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", mrb_io_initialize, MRB_ARGS_ARG(1,2)); /* 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, "sysread", mrb_io_sysread, MRB_ARGS_ARG(1,1));
+ mrb_define_method(mrb, io, "sysseek", mrb_io_sysseek, MRB_ARGS_ARG(1,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());
@@ -1332,8 +1553,10 @@ mrb_init_io(mrb_state *mrb)
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_define_method(mrb, io, "fileno", mrb_io_fileno_m, MRB_ARGS_NONE());
+ mrb_define_method(mrb, io, "pread", mrb_io_pread, MRB_ARGS_ANY()); /* ruby 2.5 feature */
+ mrb_define_method(mrb, io, "pwrite", mrb_io_pwrite, MRB_ARGS_ANY()); /* ruby 2.5 feature */
- mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$/"), mrb_str_new_cstr(mrb, "\n"));
+ mrb_define_method(mrb, io, "_readchar", mrb_io_readchar, MRB_ARGS_REQ(1));
+ mrb_define_class_method(mrb, io, "_bufread", mrb_io_bufread, MRB_ARGS_REQ(2));
}
diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb
index ca285f5bd..c13850607 100644
--- a/mrbgems/mruby-io/test/file.rb
+++ b/mrbgems/mruby-io/test/file.rb
@@ -1,16 +1,14 @@
##
-# IO Test
+# File Test
-assert('File', '15.2.21') do
- File.class == Class
-end
+MRubyIOTestUtil.io_test_setup
-assert('File', '15.2.21.2') do
- File.superclass == IO
+assert('File.class', '15.2.21') do
+ assert_equal Class, File.class
end
-assert('File TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
+assert('File.superclass', '15.2.21.2') do
+ assert_equal IO, File.superclass
end
assert('File#initialize', '15.2.21.4.1') do
@@ -27,7 +25,7 @@ assert('File#path', '15.2.21.4.2') do
assert_equal $mrbtest_io_rfname, io.path
io.close
assert_equal $mrbtest_io_rfname, io.path
- io.closed?
+ assert_true io.closed?
end
assert('File.basename') do
@@ -35,6 +33,7 @@ assert('File.basename') do
assert_equal 'a', File.basename('/a/')
assert_equal 'b', File.basename('/a/b')
assert_equal 'b', File.basename('../a/b')
+ assert_raise(ArgumentError) { File.basename("/a/b\0") }
end
assert('File.dirname') do
@@ -52,6 +51,8 @@ assert('File.extname') do
assert_equal '', File.extname('foo/.bar')
assert_equal '', File.extname('foo.txt/bar')
assert_equal '', File.extname('.foo')
+ assert_equal '.rb', File.extname('.a.rb')
+ assert_equal '.', File.extname('foo.')
end
assert('File#flock') do
@@ -69,9 +70,6 @@ assert('File#flock') do
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
@@ -84,6 +82,22 @@ assert('File#mtime') do
end
end
+assert('File#size and File#truncate') do
+ fname = "#{$mrbtest_io_wfname}.resize"
+ begin
+ File.open(fname, 'w') do |f|
+ assert_equal 0, f.size
+ assert_equal 0, f.truncate(100)
+ assert_equal 100, f.size
+ assert_equal 0, f.pos
+ assert_equal 0, f.truncate(5)
+ assert_equal 5, f.size
+ end
+ ensure
+ File.delete(fname)
+ end
+end
+
assert('File.join') do
assert_equal "", File.join()
assert_equal "a", File.join("a")
@@ -98,24 +112,32 @@ assert('File.join') do
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
+ dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
+ begin
+ sep = File::ALT_SEPARATOR || File::SEPARATOR
+ relative_path = "#{File.basename(dir)}#{sep}realpath_test"
+ path = "#{File._getwd}#{sep}#{relative_path}"
+ File.open(path, "w"){}
+ assert_equal path, File.realpath(relative_path)
+
+ unless MRubyIOTestUtil.win?
+ path1 = File.realpath($mrbtest_io_rfname)
+ path2 = File.realpath($mrbtest_io_symlinkname)
+ assert_equal path1, path2
end
+ ensure
+ File.delete path rescue nil
+ MRubyIOTestUtil.rmdir dir
end
+
+ assert_raise(ArgumentError) { File.realpath("TO\0DO") }
end
assert("File.readlink") do
begin
- assert_equal $mrbtest_io_rfname, File.readlink($mrbtest_io_symlinkname)
+ exp = File.basename($mrbtest_io_rfname)
+ act = File.readlink($mrbtest_io_symlinkname)
+ assert_equal exp, act
rescue NotImplementedError => e
skip e.message
end
@@ -177,37 +199,39 @@ assert('File.path') do
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
+
+ begin
+ tmpdir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
+ rescue => e
+ skip e.message
+ end
+
+ symlink_name = "#{tmpdir}/test-bin-dummy"
+ begin
+ assert_equal 0, File.symlink(target_name, symlink_name)
+ assert_equal true, File.symlink?(symlink_name)
+ rescue NotImplementedError => e
+ skip e.message
+ ensure
+ File.delete symlink_name rescue nil
+ MRubyIOTestUtil.rmdir tmpdir rescue nil
end
end
assert('File.chmod') do
- File.open('chmod-test', 'w') {}
+ File.open("#{$mrbtest_io_wfname}.chmod-test", 'w') {}
begin
- assert_equal 1, File.chmod(0400, 'chmod-test')
+ assert_equal 1, File.chmod(0400, "#{$mrbtest_io_wfname}.chmod-test")
ensure
- File.delete('chmod-test')
+ File.delete("#{$mrbtest_io_wfname}.chmod-test")
end
end
-assert('File TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/file_test.rb b/mrbgems/mruby-io/test/file_test.rb
index 2c831f0d5..2134c6a75 100644
--- a/mrbgems/mruby-io/test/file_test.rb
+++ b/mrbgems/mruby-io/test/file_test.rb
@@ -1,9 +1,7 @@
##
# FileTest
-assert('FileTest TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
-end
+MRubyIOTestUtil.io_test_setup
assert("FileTest.directory?") do
dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
@@ -22,9 +20,8 @@ assert("FileTest.exist?") do
assert_equal true, FileTest.exist?(io), "io obj - exist"
io.close
assert_equal true, io.closed?
- assert_raise IOError do
- FileTest.exist?(io)
- end
+ assert_raise(IOError) { FileTest.exist?(io) }
+ assert_raise(TypeError) { File.exist?($mrbtest_io_rfname.to_sym) }
end
assert("FileTest.file?") do
@@ -67,11 +64,11 @@ assert("FileTest.size?") do
assert_raise IOError do
FileTest.size?(fp1)
end
+ assert_true fp1.closed?
assert_raise IOError do
FileTest.size?(fp2)
end
-
- fp1.closed? && fp2.closed?
+ assert_true fp2.closed?
end
assert("FileTest.socket?") do
@@ -105,13 +102,11 @@ assert("FileTest.zero?") do
assert_raise IOError do
FileTest.zero?(fp1)
end
+ assert_true fp1.closed?
assert_raise IOError do
FileTest.zero?(fp2)
end
-
- fp1.closed? && fp2.closed?
+ assert_true fp2.closed?
end
-assert('FileTest TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/gc_filedes.sh b/mrbgems/mruby-io/test/gc_filedes.sh
deleted file mode 100644
index 6e5d1bbf1..000000000
--- a/mrbgems/mruby-io/test/gc_filedes.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-ulimit -n 20
-mruby -e '100.times { File.open "'$0'" }'
diff --git a/mrbgems/mruby-io/test/io.rb b/mrbgems/mruby-io/test/io.rb
index 48a74f31e..88a89fee4 100644
--- a/mrbgems/mruby-io/test/io.rb
+++ b/mrbgems/mruby-io/test/io.rb
@@ -1,57 +1,49 @@
##
# 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
+MRubyIOTestUtil.io_test_setup
+$cr, $crlf, $cmd = MRubyIOTestUtil.win? ? [1, "\r\n", "cmd /c "] : [0, "\n", ""]
+
+def assert_io_open(meth)
+ assert "assert_io_open" do
+ fd = IO.sysopen($mrbtest_io_rfname)
+ assert_equal Integer, fd.class
+ io1 = IO.__send__(meth, fd)
+ begin
+ assert_equal IO, io1.class
+ assert_equal $mrbtest_io_msg, io1.read
+ ensure
+ io1.close
+ end
+
+ io2 = IO.__send__(meth, IO.sysopen($mrbtest_io_rfname))do |io|
+ if meth == :open
+ assert_equal $mrbtest_io_msg, io.read
+ else
+ flunk "IO.#{meth} does not take block"
end
end
- ret
- end
-end
+ io2.close unless meth == :open
-assert('IO TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
- $cr = MRubyIOTestUtil.win? ? 1 : 0 # "\n" include CR or not
+ assert_raise(RuntimeError) { IO.__send__(meth, 1023) } # For Windows
+ assert_raise(RuntimeError) { IO.__send__(meth, 1 << 26) }
+ end
end
-assert('IO', '15.2.20') do
+assert('IO.class', '15.2.20') do
assert_equal(Class, IO.class)
end
-assert('IO', '15.2.20.2') do
+assert('IO.superclass', '15.2.20.2') do
assert_equal(Object, IO.superclass)
end
-assert('IO', '15.2.20.3') do
+assert('IO.ancestors', '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
+ assert_io_open(:open)
end
assert('IO#close', '15.2.20.5.1') do
@@ -92,8 +84,6 @@ assert('IO#eof?', '15.2.20.5.6') do
io.read
assert_true io.eof?
io.close
-
- true
end
assert('IO#flush', '15.2.20.5.7') do
@@ -108,12 +98,20 @@ end
assert('IO#getc', '15.2.20.5.8') do
io = IO.new(IO.sysopen($mrbtest_io_rfname))
- $mrbtest_io_msg.each_char { |ch|
+ $mrbtest_io_msg.split("").each { |ch|
assert_equal ch, io.getc
}
assert_equal nil, io.getc
io.close
- true
+end
+
+assert('IO#getbyte') do
+ io = IO.new(IO.sysopen($mrbtest_io_rfname))
+ $mrbtest_io_msg.split("").each do |ch|
+ assert_equal ch.getbyte(0), io.getbyte
+ end
+ assert_equal nil, io.getbyte
+ io.close
end
#assert('IO#gets', '15.2.20.5.9') do
@@ -143,16 +141,17 @@ 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
+ IO.pipe do |r,w|
+ n = IO::BUF_SIZE+1
+ w.write 'a'*n
+ assert_equal 'a'*n, r.read(n)
+ end
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|
+ $mrbtest_io_msg.split("").each { |ch|
assert_equal ch, io.readchar
}
assert_raise(EOFError) do
@@ -199,8 +198,6 @@ assert('IO#write', '15.2.20.5.20') do
io.rewind
assert_equal "ab123fg", io.read
io.close
-
- true
end
assert('IO#<<') do
@@ -208,7 +205,6 @@ assert('IO#<<') do
io << "" << ""
assert_equal 0, io.pos
io.close
- true
end
assert('IO#dup for readable') do
@@ -228,7 +224,6 @@ assert('IO#dup for readable') do
dup.close
assert_false io.closed?
io.close
- true
end
assert('IO#dup for writable') do
@@ -241,25 +236,18 @@ assert('IO#dup for writable') do
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
+ assert_io_open(:for_fd)
end
assert('IO.new') do
- io = IO.new(0)
- io.close
- true
+ assert_io_open(:new)
end
assert('IO gc check') do
- 100.times { IO.new(0) }
+ assert_nothing_raised { 100.times { IO.new(0) } }
end
assert('IO.sysopen("./nonexistent")') do
@@ -300,7 +288,6 @@ assert('IO.sysopen, IO#sysread') do
io = IO.new fd, "w"
assert_raise(IOError) { io.sysread(1) }
io.close
- true
end
assert('IO.sysopen, IO#syswrite') do
@@ -314,8 +301,6 @@ assert('IO.sysopen, IO#syswrite') do
io = IO.new(IO.sysopen($mrbtest_io_rfname), "r")
assert_raise(IOError) { io.syswrite("a") }
io.close
-
- true
end
assert('IO#_read_buf') do
@@ -339,20 +324,25 @@ assert('IO#_read_buf') do
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
+ begin
+ f = File.open("/dev/tty")
+ rescue RuntimeError => e
+ skip e.message
+ else
+ assert_true f.isatty
+ ensure
+ f&.close
+ end
+ begin
+ f = File.open($mrbtest_io_rfname)
+ assert_false f.isatty
+ ensure
+ f&.close
+ end
end
assert('IO#pos=, IO#seek') do
@@ -366,7 +356,6 @@ assert('IO#pos=, IO#seek') do
assert_equal 0, io.seek(0)
assert_equal 0, io.pos
io.close
- io.closed?
end
assert('IO#rewind') do
@@ -377,7 +366,6 @@ assert('IO#rewind') do
assert_equal 0, io.rewind
assert_equal 0, io.pos
io.close
- io.closed?
end
assert('IO#gets') do
@@ -426,7 +414,6 @@ assert('IO#gets') do
assert_equal nil, io.gets, "gets third line; returns nil"
io.close
- io.closed?
end
assert('IO#gets - paragraph mode') do
@@ -437,7 +424,6 @@ assert('IO#gets - paragraph mode') do
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
@@ -448,15 +434,14 @@ assert('IO#gets - paragraph mode') do
text2 = io.gets("")
assert_equal para2, text2
io.close
- io.closed?
end
assert('IO.popen') do
begin
$? = nil
- io = IO.popen("echo mruby-io")
+ io = IO.popen("#{$cmd}echo mruby-io")
assert_true io.close_on_exec?
- assert_equal Fixnum, io.pid.class
+ assert_equal Integer, io.pid.class
out = io.read
assert_equal out.class, String
@@ -542,14 +527,13 @@ assert('IO#fileno') do
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.
+ # IO.sysopen opens a file descriptor with O_CLOEXEC flag.
assert_true io.close_on_exec?
rescue ScriptError
io.close
@@ -564,7 +548,6 @@ assert('IO#close_on_exec') do
assert_equal(false, io.close_on_exec?)
io.close
- io.closed?
begin
r, w = IO.pipe
@@ -593,6 +576,34 @@ assert('IO#sysseek') do
end
end
+assert('IO#pread') do
+ skip "IO#pread is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE
+
+ IO.open(IO.sysopen($mrbtest_io_rfname, 'r'), 'r') do |io|
+ assert_equal $mrbtest_io_msg.byteslice(5, 8), io.pread(8, 5)
+ assert_equal 0, io.pos
+ assert_equal $mrbtest_io_msg.byteslice(1, 5), io.pread(5, 1)
+ assert_equal 0, io.pos
+ assert_raise(RuntimeError) { io.pread(20, -9) }
+ end
+end
+
+assert('IO#pwrite') do
+ skip "IO#pwrite is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE
+
+ IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+') do |io|
+ assert_equal 6, io.pwrite("Warld!", 7)
+ assert_equal 0, io.pos
+ assert_equal 7, io.pwrite("Hello, ", 0)
+ assert_equal 0, io.pos
+ assert_equal "Hello, Warld!", io.read
+ assert_equal 6, io.pwrite("world!", 7)
+ assert_equal 13, io.pos
+ io.pos = 0
+ assert_equal "Hello, world!", io.read
+ end
+end
+
assert('IO.pipe') do
begin
called = false
@@ -635,12 +646,10 @@ end
assert('`cmd`') do
begin
- assert_equal `echo foo`, "foo\n"
+ assert_equal `#{$cmd}echo foo`, "foo#{$crlf}"
rescue NotImplementedError => e
skip e.message
end
end
-assert('IO TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/mruby_io_test.c b/mrbgems/mruby-io/test/mruby_io_test.c
index 71239a827..cf99260e2 100644
--- a/mrbgems/mruby-io/test/mruby_io_test.c
+++ b/mrbgems/mruby-io/test/mruby_io_test.c
@@ -1,5 +1,7 @@
+#include <mruby/common.h>
#include <sys/types.h>
#include <errno.h>
+#include <string.h>
#if defined(_WIN32) || defined(_WIN64)
@@ -7,7 +9,6 @@
#include <io.h>
#include <fcntl.h>
#include <direct.h>
-#include <string.h>
#include <stdlib.h>
#include <malloc.h>
@@ -18,7 +19,9 @@ typedef int mode_t;
#define open _open
#define close _close
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || \
+ (defined(MRB_MINGW32_VERSION) && MRB_MINGW32_VERSION < 3021) || \
+ (defined(MRB_MINGW64_VERSION) && MRB_MINGW64_VERSION < 4000)
#include <sys/stat.h>
static int
@@ -50,10 +53,11 @@ mkdtemp(char *temp)
#include <sys/socket.h>
#include <unistd.h>
#include <sys/un.h>
+ #include <fcntl.h>
+ #include <libgen.h>
#endif
#include <sys/stat.h>
-#include <stdio.h>
#include <stdlib.h>
#include "mruby.h"
@@ -61,51 +65,55 @@ mkdtemp(char *temp)
#include "mruby/error.h"
#include "mruby/string.h"
#include "mruby/variable.h"
+#include <mruby/ext/io.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";
+#define GVNAME(n) "$mrbtest_io_" #n "name"
+ enum {IDX_READ, IDX_WRITE, IDX_LINK, IDX_SOCKET, IDX_COUNT};
+ const char *gvnames[] = {GVNAME(rf), GVNAME(wf), GVNAME(symlink), GVNAME(socket)};
+ char *fnames[IDX_COUNT];
+ int fds[IDX_COUNT];
char msg[] = "mruby io test\n";
mode_t mask;
- int fd0, fd1;
FILE *fp;
-
+ int i;
#if !defined(_WIN32) && !defined(_WIN64)
- int fd2, fd3;
- struct sockaddr_un sun0;
+ struct sockaddr_un sun0 = { 0 }; /* Initialize them all because it is environment dependent */
#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);
+ mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_str_new_cstr(mrb, msg));
+ mask = umask(077);
+ for (i = 0; i < IDX_COUNT; i++) {
+ mrb_value fname = mrb_str_new_capa(mrb, 0);
#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();
- }
+ /*
+ * Workaround for not being able to bind a socket to some file systems
+ * (e.g. vboxsf, NFS). [#4981]
+ */
+ char *tmpdir = getenv("TMPDIR");
+ if (tmpdir && strlen(tmpdir) > 0) {
+ mrb_str_cat_cstr(mrb, fname, tmpdir);
+ if (*(RSTRING_END(fname)-1) != '/') mrb_str_cat_lit(mrb, fname, "/");
+ } else {
+ mrb_str_cat_lit(mrb, fname, "/tmp/");
+ }
#endif
+ mrb_str_cat_cstr(mrb, fname, gvnames[i]+1);
+ mrb_str_cat_cstr(mrb, fname, ".XXXXXXXX");
+ fnames[i] = RSTRING_PTR(fname);
+ fds[i] = mkstemp(fnames[i]);
+ if (fds[i] == -1) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file");
+ }
+ close(fds[i]);
+ mrb_gv_set(mrb, mrb_intern_cstr(mrb, gvnames[i]), fname);
+ }
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");
+ fp = fopen(fnames[IDX_READ], "wb");
if (fp == NULL) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
return mrb_nil_value();
@@ -113,7 +121,7 @@ mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
fputs(msg, fp);
fclose(fp);
- fp = fopen(wfname, "wb");
+ fp = fopen(fnames[IDX_WRITE], "wb");
if (fp == NULL) {
mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
return mrb_nil_value();
@@ -121,29 +129,29 @@ mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
fclose(fp);
#if !defined(_WIN32) && !defined(_WIN64)
- unlink(symlinkname);
- close(fd2);
- if (symlink(rfname, symlinkname) == -1) {
+ unlink(fnames[IDX_LINK]);
+ if (symlink(basename(fnames[IDX_READ]), fnames[IDX_LINK]) == -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) {
+ unlink(fnames[IDX_SOCKET]);
+ fds[IDX_SOCKET] = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fds[IDX_SOCKET] == -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));
+ strncpy(sun0.sun_path, fnames[IDX_SOCKET], sizeof(sun0.sun_path)-1);
+ sun0.sun_path[sizeof(sun0.sun_path)-1] = 0;
+ if (bind(fds[IDX_SOCKET], (struct sockaddr *)&sun0, sizeof(sun0)) == -1) {
+ mrb_raisef(mrb, E_RUNTIME_ERROR, "can't bind AF_UNIX socket to %s: %d",
+ sun0.sun_path,
+ errno);
}
- close(fd3);
+ close(fds[IDX_SOCKET]);
#endif
return mrb_true_value();
+#undef GVNAME
}
static mrb_value
@@ -154,16 +162,16 @@ mrb_io_test_io_cleanup(mrb_state *mrb, mrb_value self)
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) {
+ if (mrb_string_p(rfname)) {
remove(RSTRING_PTR(rfname));
}
- if (mrb_type(wfname) == MRB_TT_STRING) {
+ if (mrb_string_p(wfname)) {
remove(RSTRING_PTR(wfname));
}
- if (mrb_type(symlinkname) == MRB_TT_STRING) {
+ if (mrb_string_p(symlinkname)) {
remove(RSTRING_PTR(symlinkname));
}
- if (mrb_type(socketname) == MRB_TT_STRING) {
+ if (mrb_string_p(socketname)) {
remove(RSTRING_PTR(socketname));
}
@@ -177,28 +185,6 @@ mrb_io_test_io_cleanup(mrb_state *mrb, mrb_value self)
}
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;
@@ -215,18 +201,16 @@ mrb_io_test_mkdtemp(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_io_test_rmdir(mrb_state *mrb, mrb_value klass)
{
- mrb_value str;
- char *cp;
+ const char *cp;
- mrb_get_args(mrb, "S", &str);
- cp = mrb_str_to_cstr(mrb, str);
+ mrb_get_args(mrb, "z", &cp);
if (rmdir(cp) == -1) {
mrb_sys_fail(mrb, "rmdir");
}
return mrb_true_value();
}
-mrb_value
+static mrb_value
mrb_io_win_p(mrb_state *mrb, mrb_value klass)
{
#if defined(_WIN32) || defined(_WIN64)
@@ -240,6 +224,12 @@ mrb_io_win_p(mrb_state *mrb, mrb_value klass)
#endif
}
+#ifdef MRB_WITH_IO_PREAD_PWRITE
+# define MRB_WITH_IO_PREAD_PWRITE_ENABLED TRUE
+#else
+# define MRB_WITH_IO_PREAD_PWRITE_ENABLED FALSE
+#endif
+
void
mrb_mruby_io_gem_test(mrb_state* mrb)
{
@@ -247,10 +237,9 @@ mrb_mruby_io_gem_test(mrb_state* mrb)
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());
+
+ mrb_define_const(mrb, io_test, "MRB_WITH_IO_PREAD_PWRITE", mrb_bool_value(MRB_WITH_IO_PREAD_PWRITE_ENABLED));
}
diff --git a/mrbgems/mruby-kernel-ext/mrbgem.rake b/mrbgems/mruby-kernel-ext/mrbgem.rake
index fcb3a83b0..e52c63a55 100644
--- a/mrbgems/mruby-kernel-ext/mrbgem.rake
+++ b/mrbgems/mruby-kernel-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'Kernel module extension'
+ spec.summary = 'extensional function-like methods'
end
diff --git a/mrbgems/mruby-kernel-ext/mrblib/kernel.rb b/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
deleted file mode 100644
index bf739ed1a..000000000
--- a/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Kernel
- # call-seq:
- # obj.yield_self {|_obj|...} -> an_object
- # obj.then {|_obj|...} -> an_object
- #
- # Yields <i>obj</i> 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
diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c
index 32d86376a..15b4c5f72 100644
--- a/mrbgems/mruby-kernel-ext/src/kernel.c
+++ b/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -3,62 +3,63 @@
#include <mruby/array.h>
#include <mruby/hash.h>
#include <mruby/range.h>
+#include <mruby/presym.h>
static mrb_value
mrb_f_caller(mrb_state *mrb, mrb_value self)
{
- mrb_value bt, v, length;
+ mrb_value bt, v;
mrb_int bt_len, argc, lev, n;
+ argc = mrb_get_args(mrb, "|oi", &v, &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();
- }
+ case 0:
+ lev = 1;
+ n = bt_len - 1;
+ break;
+ case 1:
+ if (mrb_range_p(v)) {
+ mrb_int beg, len;
+ if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == MRB_RANGE_OK) {
+ lev = beg;
+ n = len;
}
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;
+ return mrb_nil_value();
}
- break;
- case 2:
- lev = mrb_fixnum(mrb_to_int(mrb, v));
- n = mrb_fixnum(mrb_to_int(mrb, length));
+ }
+ else {
+ lev = mrb_as_int(mrb, v);
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);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%v)", v);
}
- break;
- default:
- lev = n = 0;
- break;
+ n = bt_len - lev;
+ }
+ break;
+ case 2:
+ lev = mrb_as_int(mrb, v);
+ break;
+ default:
+ /* not reached */
+ lev = n = 0;
+ break;
}
-
- if (n == 0) {
+ if (lev >= bt_len) return mrb_nil_value();
+ if (lev < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%v)", v);
+ }
+ if (n < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%d)", n);
+ }
+ if (n == 0 || bt_len <= lev) {
return mrb_ary_new(mrb);
}
-
- return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n));
+ if (bt_len <= n + lev) n = bt_len - lev - 1;
+ return mrb_ary_new_from_values(mrb, n, RARRAY_PTR(bt)+lev+1);
}
/*
@@ -85,17 +86,16 @@ mrb_f_method(mrb_state *mrb, mrb_value self)
* call-seq:
* Integer(arg,base=0) -> integer
*
- * Converts <i>arg</i> to a <code>Fixnum</code>.
- * Numeric types are converted directly (with floating point numbers
+ * Converts <i>arg</i> to a <code>Integer</code>.
+ * Numeric types are converted directly (with floating-point numbers
* being truncated). <i>base</i> (0, or between 2 and 36) is a base for
* integer string representation. If <i>arg</i> is a <code>String</code>,
* when <i>base</i> is omitted or equals to zero, radix indicators
* (<code>0</code>, <code>0b</code>, and <code>0x</code>) are honored.
* In any case, strings should be strictly conformed to numeric
* representation. This behavior is different from that of
- * <code>String#to_i</code>. Non string values will be converted using
- * <code>to_int</code>, and <code>to_i</code>. Passing <code>nil</code>
- * raises a TypeError.
+ * <code>String#to_i</code>. Non string values will be treated as integers.
+ * Passing <code>nil</code> raises a TypeError.
*
* Integer(123.999) #=> 123
* Integer("0x1a") #=> 26
@@ -114,7 +114,7 @@ mrb_f_integer(mrb_state *mrb, mrb_value self)
return mrb_convert_to_integer(mrb, arg, base);
}
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
/*
* call-seq:
* Float(arg) -> float
@@ -130,10 +130,9 @@ mrb_f_integer(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_f_float(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
+ mrb_value arg = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &arg);
- return mrb_Float(mrb, arg);
+ return mrb_to_float(mrb, arg);
}
#endif
@@ -142,8 +141,7 @@ mrb_f_float(mrb_state *mrb, mrb_value self)
* String(arg) -> string
*
* Returns <i>arg</i> as an <code>String</code>.
- *
- * First tries to call its <code>to_str</code> method, then its to_s method.
+ * converted using <code>to_s</code> method.
*
* String(self) #=> "main"
* String(self.class) #=> "Object"
@@ -152,13 +150,10 @@ mrb_f_float(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_f_string(mrb_state *mrb, mrb_value self)
{
- mrb_value arg, tmp;
+ mrb_value arg = mrb_get_arg1(mrb);
+ mrb_value tmp;
- mrb_get_args(mrb, "o", &arg);
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_str");
- if (mrb_nil_p(tmp)) {
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
- }
+ tmp = mrb_type_convert(mrb, arg, MRB_TT_STRING, MRB_SYM(to_s));
return tmp;
}
@@ -166,9 +161,7 @@ mrb_f_string(mrb_state *mrb, mrb_value self)
* call-seq:
* Array(arg) -> array
*
- * Returns +arg+ as an Array.
- *
- * First tries to call Array#to_ary on +arg+, then Array#to_a.
+ * Returns +arg+ as an Array using to_a method.
*
* Array(1..5) #=> [1, 2, 3, 4, 5]
*
@@ -176,13 +169,10 @@ mrb_f_string(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_f_array(mrb_state *mrb, mrb_value self)
{
- mrb_value arg, tmp;
+ mrb_value arg = mrb_get_arg1(mrb);
+ mrb_value tmp;
- mrb_get_args(mrb, "o", &arg);
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_ary");
- if (mrb_nil_p(tmp)) {
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a");
- }
+ tmp = mrb_type_convert_check(mrb, arg, MRB_TT_ARRAY, MRB_SYM(to_a));
if (mrb_nil_p(tmp)) {
return mrb_ary_new_from_values(mrb, 1, &arg);
}
@@ -194,9 +184,9 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
* call-seq:
* Hash(arg) -> hash
*
- * Converts <i>arg</i> to a <code>Hash</code> by calling
- * <i>arg</i><code>.to_hash</code>. Returns an empty <code>Hash</code> when
- * <i>arg</i> is <tt>nil</tt> or <tt>[]</tt>.
+ * Returns a <code>Hash</code> if <i>arg</i> is a <code>Hash</code>.
+ * Returns an empty <code>Hash</code> when <i>arg</i> is <tt>nil</tt>
+ * or <tt>[]</tt>.
*
* Hash([]) #=> {}
* Hash(nil) #=> {}
@@ -207,37 +197,12 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_f_hash(mrb_state *mrb, mrb_value self)
{
- mrb_value arg, tmp;
+ mrb_value arg = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &arg);
- if (mrb_nil_p(arg)) {
+ if (mrb_nil_p(arg) || (mrb_array_p(arg) && RARRAY_LEN(arg) == 0)) {
return mrb_hash_new(mrb);
}
- tmp = mrb_check_convert_type(mrb, arg, MRB_TT_HASH, "Hash", "to_hash");
- if (mrb_nil_p(tmp)) {
- if (mrb_array_p(arg) && RARRAY_LEN(arg) == 0) {
- return mrb_hash_new(mrb);
- }
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into Hash",
- mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, arg)));
- }
- return tmp;
-}
-
-/*
- * call-seq:
- * obj.itself -> an_object
- *
- * Returns <i>obj</i>.
- *
- * 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;
+ return mrb_ensure_hash_type(mrb, arg);
}
void
@@ -248,14 +213,13 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
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, "Integer", mrb_f_integer, MRB_ARGS_ARG(1,1));
+#ifndef MRB_NO_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
diff --git a/mrbgems/mruby-kernel-ext/test/kernel.rb b/mrbgems/mruby-kernel-ext/test/kernel.rb
index 206b7ac74..fc4402b3d 100644
--- a/mrbgems/mruby-kernel-ext/test/kernel.rb
+++ b/mrbgems/mruby-kernel-ext/test/kernel.rb
@@ -20,6 +20,9 @@ assert('Kernel.caller, Kernel#caller') do
bar(*args)
end
end
+
+ skip "backtrace isn't available" if (c.new.baz(0)[0].include?("unknown"))
+
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]
@@ -49,21 +52,53 @@ assert('Kernel#__method__') do
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_operator(26, :eql?, Integer("0x1a"))
+ assert_operator(930, :eql?, Integer("0930", 10))
+ assert_operator(7, :eql?, Integer("111", 2))
+ assert_operator(0, :eql?, Integer("0"))
+ assert_operator(0, :eql?, Integer("00000"))
+ assert_operator(123, :eql?, Integer('1_2_3'))
+ assert_operator(123, :eql?, Integer("\t\r\n\f\v 123 \t\r\n\f\v"))
assert_raise(TypeError) { Integer(nil) }
+ assert_raise(ArgumentError) { Integer('a') }
+ assert_raise(ArgumentError) { Integer('4a5') }
+ assert_raise(ArgumentError) { Integer('1_2__3') }
+ assert_raise(ArgumentError) { Integer('68_') }
+ assert_raise(ArgumentError) { Integer('68_ ') }
+ assert_raise(ArgumentError) { Integer('_68') }
+ assert_raise(ArgumentError) { Integer(' _68') }
+ assert_raise(ArgumentError) { Integer('6 8') }
+ assert_raise(ArgumentError) { Integer("15\0") }
+ assert_raise(ArgumentError) { Integer("15.0") }
+ skip unless Object.const_defined?(:Float)
+ assert_operator(123, :eql?, Integer(123.999))
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"))
+ skip unless Object.const_defined?(:Float)
+ assert_operator(1.0, :eql?, Float(1))
+ assert_operator(123.456, :eql?, Float(123.456))
+ assert_operator(123.456, :eql?, Float("123.456"))
+ assert_operator(123.0, :eql?, Float('1_2_3'))
+ assert_operator(12.34, :eql?, Float('1_2.3_4'))
+ assert_operator(0.9, :eql?, Float('.9'))
+ assert_operator(0.9, :eql?, Float(" \t\r\n\f\v.9 \t\r\n\f\v"))
+ assert_operator(16.0, :eql?, Float("0x10"))
assert_raise(TypeError) { Float(nil) }
-end if class_defined?("Float")
+ assert_raise(ArgumentError) { Float("1. 5") }
+ assert_raise(ArgumentError) { Float("1.5a") }
+ assert_raise(ArgumentError) { Float("1.5\0") }
+ assert_raise(ArgumentError) { Float('a') }
+ assert_raise(ArgumentError) { Float('4a5') }
+ assert_raise(ArgumentError) { Float('1_2__3') }
+ assert_raise(ArgumentError) { Float('68_') }
+ assert_raise(ArgumentError) { Float('68._7') }
+ assert_raise(ArgumentError) { Float('68.7_') }
+ assert_raise(ArgumentError) { Float('68.7_ ') }
+ assert_raise(ArgumentError) { Float('_68') }
+ assert_raise(ArgumentError) { Float(' _68') }
+ assert_raise(ArgumentError) { Float('1_2.3__4') }
+end
assert('Kernel#String') do
assert_equal("main", String(self))
diff --git a/mrbgems/mruby-math/src/math.c b/mrbgems/mruby-math/src/math.c
index c182debea..0b85b1763 100644
--- a/mrbgems/mruby-math/src/math.c
+++ b/mrbgems/mruby-math/src/math.c
@@ -5,17 +5,21 @@
*/
#include <mruby.h>
-#include <mruby/array.h>
+#ifdef MRB_NO_FLOAT
+# error Math conflicts with 'MRB_NO_FLOAT' configuration
+#endif
+
+#include <mruby/array.h>
+#include <mruby/presym.h>
#include <math.h>
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);
+ struct RClass *math = mrb_module_get_id(mrb, MRB_SYM(Math));
+ struct RClass *domainerror = mrb_class_get_under_id(mrb, math, MRB_SYM(DomainError));
+ mrb_raisef(mrb, domainerror, "Numerical argument is out of domain - %s", func);
}
/* math functions not provided by Microsoft Visual C++ 2012 or older */
@@ -23,8 +27,6 @@ domain_error(mrb_state *mrb, const char *func)
#include <float.h>
-#define MATH_TOLERANCE 1E-12
-
double
asinh(double x)
{
@@ -122,7 +124,8 @@ erf(double x)
term *= xsqr/j;
sum += term/(2*j+1);
++j;
- } while (fabs(term/sum) > MATH_TOLERANCE);
+ if (sum == 0) break;
+ } while (fabs(term/sum) > DBL_EPSILON);
return two_sqrtpi*sum;
}
@@ -155,12 +158,16 @@ erfc(double x)
n += 0.5;
q1 = q2;
q2 = b/d;
- } while (fabs(q1-q2)/q2 > MATH_TOLERANCE);
+ } while (fabs(q1-q2)/q2 > DBL_EPSILON);
return one_sqrtpi*exp(-x*x)*q2;
}
#endif
+#if defined __FreeBSD__ && !defined __FreeBSD_version
+#include <osreldate.h> /* for __FreeBSD_version */
+#endif
+
#if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__ && __FreeBSD_version < 803000)
double
@@ -623,7 +630,7 @@ math_cbrt(mrb_state *mrb, mrb_value obj)
* Math.frexp(numeric) -> [ fraction, exponent ]
*
* Returns a two-element array containing the normalized fraction (a
- * <code>Float</code>) and exponent (a <code>Fixnum</code>) of
+ * <code>Float</code>) and exponent (a <code>Integer</code>) of
* <i>numeric</i>.
*
* fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11]
@@ -724,24 +731,18 @@ 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);
+ mrb_define_class_under_id(mrb, mrb_math, MRB_SYM(DomainError), mrb->eStandardError_class);
#ifdef M_PI
- mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, M_PI));
+ mrb_define_const_id(mrb, mrb_math, MRB_SYM(PI), mrb_float_value(mrb, M_PI));
#else
- mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, atan(1.0)*4.0));
+ mrb_define_const_id(mrb, mrb_math, MRB_SYM(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));
+ mrb_define_const_id(mrb, mrb_math, MRB_SYM(E), mrb_float_value(mrb, M_E));
#else
- mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-12));
+ mrb_define_const_id(mrb, mrb_math, MRB_SYM(E), mrb_float_value(mrb, exp(1.0)));
#endif
mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-math/test/math.rb b/mrbgems/mruby-math/test/math.rb
index e9ea07cc1..4db63bbf9 100644
--- a/mrbgems/mruby-math/test/math.rb
+++ b/mrbgems/mruby-math/test/math.rb
@@ -1,46 +1,30 @@
##
# 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
+def assert_float_and_int(exp_ary, act_ary)
+ assert('assert_float_and_int') do
+ flo_exp, int_exp, flo_act, int_act = *exp_ary, *act_ary
+ assert_float(flo_exp, flo_act)
+ assert_operator(int_exp, :eql?, int_act)
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)
+assert('Math.sin') do
+ assert_float(0, Math.sin(0))
+ assert_float(1, Math.sin(Math::PI / 2))
end
-assert('Math.tan 0') do
- check_float(Math.tan(0), 0)
+assert('Math.cos') do
+ assert_float(1, Math.cos(0))
+ assert_float(0, Math.cos(Math::PI / 2))
end
-assert('Math.tan PI/4') do
- check_float(Math.tan(Math::PI / 4), 1)
+assert('Math.tan') do
+ assert_float(0, Math.tan(0))
+ assert_float(1, Math.tan(Math::PI / 4))
end
assert('Fundamental trig identities') do
- result = true
N = 13
N.times do |i|
a = Math::PI / N * i
@@ -48,105 +32,170 @@ assert('Fundamental trig identities') do
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)
+ assert_float(Math.cos(ca), s)
+ assert_float(1 / Math.tan(ca), t)
+ assert_float(1, s ** 2 + c ** 2)
+ assert_float((1/c) ** 2, t ** 2 + 1)
+ assert_float((1/s) ** 2, (1/t) ** 2 + 1)
end
- result
end
-assert('Math.erf 0') do
- check_float(Math.erf(0), 0)
+assert('Math.exp') do
+ assert_float(1.0, Math.exp(0))
+ assert_float(2.718281828459045, Math.exp(1))
+ assert_float(4.4816890703380645, Math.exp(1.5))
end
-assert('Math.exp 0') do
- check_float(Math.exp(0), 1.0)
+assert('Math.log') do
+ assert_float(0, Math.log(1))
+ assert_float(1.0, Math.log(Math::E))
+ assert_float(3.0, Math.log(Math::E**3))
end
-assert('Math.exp 1') do
- check_float(Math.exp(1), 2.718281828459045)
+assert('Math.log2') do
+ assert_float(0.0, Math.log2(1))
+ assert_float(1.0, Math.log2(2))
end
-assert('Math.exp 1.5') do
- check_float(Math.exp(1.5), 4.4816890703380645)
+assert('Math.log10') do
+ assert_float(0.0, Math.log10(1))
+ assert_float(1.0, Math.log10(10))
+ assert_float(30.0, Math.log10(10.0**30))
end
-assert('Math.log 1') do
- check_float(Math.log(1), 0)
+assert('Math.sqrt') do
+ num = [0.0, 1.0, 2.0, 3.0, 4.0]
+ sqr = [0, 1, 4, 9, 16]
+ sqr.each_with_index do |v,i|
+ assert_float(num[i], Math.sqrt(v))
+ end
end
-assert('Math.log E') do
- check_float(Math.log(Math::E), 1.0)
+assert('Math.cbrt') do
+ num = [-2.0, -1.0, 0.0, 1.0, 2.0]
+ cub = [-8, -1, 0, 1, 8]
+ cub.each_with_index do |v,i|
+ assert_float(num[i], Math.cbrt(v))
+ end
end
-assert('Math.log E**3') do
- check_float(Math.log(Math::E**3), 3.0)
+assert('Math.hypot') do
+ assert_float(5.0, Math.hypot(3, 4))
end
-assert('Math.log2 1') do
- check_float(Math.log2(1), 0.0)
+assert('Math.erf') do
+ assert_float(0, Math.erf(0))
+ assert_float(0.842700792949715, Math.erf(1))
+ assert_float(-0.8427007929497148, Math.erf(-1))
end
-assert('Math.log2 2') do
- check_float(Math.log2(2), 1.0)
+assert('Math.erfc') do
+ assert_float(0.157299207050285, Math.erfc(1))
+ assert_float(1.8427007929497148, Math.erfc(-1))
end
-assert('Math.log10 1') do
- check_float(Math.log10(1), 0.0)
+assert('Math.acos') do
+ assert_float(0 * Math::PI / 4, Math.acos( 1.0))
+ assert_float(1 * Math::PI / 4, Math.acos( 1.0 / Math.sqrt(2)))
+ assert_float(2 * Math::PI / 4, Math.acos( 0.0))
+ assert_float(4 * Math::PI / 4, Math.acos(-1.0))
+ assert_raise(Math::DomainError) { Math.acos(+1.1) }
+ assert_raise(Math::DomainError) { Math.acos(-1.1) }
end
-assert('Math.log10 10') do
- check_float(Math.log10(10), 1.0)
+assert('Math.asin') do
+ assert_float( 0 * Math::PI / 4, Math.asin( 0.0))
+ assert_float( 1 * Math::PI / 4, Math.asin( 1.0 / Math.sqrt(2)))
+ assert_float( 2 * Math::PI / 4, Math.asin( 1.0))
+ assert_float(-2 * Math::PI / 4, Math.asin(-1.0))
+ assert_raise(Math::DomainError) { Math.asin(+1.1) }
+ assert_raise(Math::DomainError) { Math.asin(-1.1) }
+ assert_raise(Math::DomainError) { Math.asin(2.0) }
end
-assert('Math.log10 10**100') do
- check_float(Math.log10(10**100), 100.0)
+assert('Math.atan') do
+ assert_float( 0 * Math::PI / 4, Math.atan( 0.0))
+ assert_float( 1 * Math::PI / 4, Math.atan( 1.0))
+ assert_float( 2 * Math::PI / 4, Math.atan(1.0 / 0.0))
+ assert_float(-1 * Math::PI / 4, Math.atan(-1.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
+assert('Math.cosh') do
+ assert_float(1, Math.cosh(0))
+ assert_float((Math::E ** 1 + Math::E ** -1) / 2, Math.cosh(1))
+ assert_float((Math::E ** 2 + Math::E ** -2) / 2, Math.cosh(2))
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
+assert('Math.sinh') do
+ assert_float(0, Math.sinh(0))
+ assert_float((Math::E ** 1 - Math::E ** -1) / 2, Math.sinh(1))
+ assert_float((Math::E ** 2 - Math::E ** -2) / 2, Math.sinh(2))
end
-assert('Math.hypot') do
- check_float(Math.hypot(3, 4), 5.0)
+assert('Math.tanh') do
+ assert_float(Math.sinh(0) / Math.cosh(0), Math.tanh(0))
+ assert_float(Math.sinh(1) / Math.cosh(1), Math.tanh(1))
+ assert_float(Math.sinh(2) / Math.cosh(2), Math.tanh(2))
+ assert_float(+1.0, Math.tanh(+1000.0))
+ assert_float(-1.0, Math.tanh(-1000.0))
end
-assert('Math.frexp 1234') do
- n = 1234
- fraction, exponent = Math.frexp(n)
- check_float(Math.ldexp(fraction, exponent), n)
+assert('Math.acosh') do
+ assert_float(0, Math.acosh(1))
+ assert_float(1, Math.acosh((Math::E ** 1 + Math::E ** -1) / 2))
+ assert_float(2, Math.acosh((Math::E ** 2 + Math::E ** -2) / 2))
+ assert_raise(Math::DomainError) { Math.acosh(0.9) }
+ assert_raise(Math::DomainError) { Math.acosh(0) }
end
-assert('Math.erf 1') do
- check_float(Math.erf(1), 0.842700792949715)
+assert('Math.asinh') do
+ assert_float(0, Math.asinh(0))
+ assert_float(1, Math.asinh((Math::E ** 1 - Math::E ** -1) / 2))
+ assert_float(2, Math.asinh((Math::E ** 2 - Math::E ** -2) / 2))
end
-assert('Math.erfc 1') do
- check_float(Math.erfc(1), 0.157299207050285)
+assert('Math.atanh') do
+ assert_float(0, Math.atanh(Math.sinh(0) / Math.cosh(0)))
+ assert_float(1, Math.atanh(Math.sinh(1) / Math.cosh(1)))
+ assert_float(2, Math.atanh(Math.sinh(2) / Math.cosh(2)))
+ assert_float(Float::INFINITY, Math.atanh(1))
+ assert_float(-Float::INFINITY, Math.atanh(-1))
+ assert_raise(Math::DomainError) { Math.atanh(+1.1) }
+ assert_raise(Math::DomainError) { Math.atanh(-1.1) }
end
-assert('Math.erf -1') do
- check_float(Math.erf(-1), -0.8427007929497148)
+assert('Math.atan2') do
+ assert_float(+0.0, Math.atan2(+0.0, +0.0))
+ assert_float(-0.0, Math.atan2(-0.0, +0.0))
+ assert_float(+Math::PI, Math.atan2(+0.0, -0.0))
+ assert_float(-Math::PI, Math.atan2(-0.0, -0.0))
+
+ assert_float(0, Math.atan2(0, 1))
+ assert_float(Math::PI / 4, Math.atan2(1, 1))
+ assert_float(Math::PI / 2, Math.atan2(1, 0))
+
+ inf = Float::INFINITY
+ skip "Math.atan2() return NaN" if Math.atan2(+inf, -inf).nan?
+ expected = 3.0 * Math::PI / 4.0
+ assert_float(+expected, Math.atan2(+inf, -inf))
+ assert_float(-expected, Math.atan2(-inf, -inf))
+ expected = Math::PI / 4.0
+ assert_float(+expected, Math.atan2(+inf, +inf))
+ assert_float(-expected, Math.atan2(-inf, +inf))
+end
+
+assert('Math.ldexp') do
+ assert_float(0.0, Math.ldexp(0.0, 0.0))
+ assert_float(0.5, Math.ldexp(0.5, 0.0))
+ assert_float(1.0, Math.ldexp(0.5, 1.0))
+ assert_float(2.0, Math.ldexp(0.5, 2.0))
+ assert_float(3.0, Math.ldexp(0.75, 2.0))
end
-assert('Math.erfc -1') do
- check_float(Math.erfc(-1), 1.8427007929497148)
+assert('Math.frexp') do
+ assert_float_and_int([0.0, 0], Math.frexp(0.0))
+ assert_float_and_int([0.5, 0], Math.frexp(0.5))
+ assert_float_and_int([0.5, 1], Math.frexp(1.0))
+ assert_float_and_int([0.5, 2], Math.frexp(2.0))
+ assert_float_and_int([0.75, 2], Math.frexp(3.0))
end
diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c
index 25c153cee..4bf31cbeb 100644
--- a/mrbgems/mruby-metaprog/src/metaprog.c
+++ b/mrbgems/mruby-metaprog/src/metaprog.c
@@ -5,6 +5,7 @@
#include "mruby/proc.h"
#include "mruby/class.h"
#include "mruby/string.h"
+#include "mruby/presym.h"
typedef enum {
NOEX_PUBLIC = 0x00,
@@ -20,6 +21,8 @@ typedef enum {
NOEX_RESPONDS = 0x80
} mrb_method_flag_t;
+mrb_value mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc);
+
static mrb_value
mrb_f_nil(mrb_state *mrb, mrb_value cv)
{
@@ -117,6 +120,7 @@ mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
}
/* 15.3.1.2.7 */
+/* 15.3.1.3.28 */
/*
* call-seq:
* local_variables -> array
@@ -131,90 +135,71 @@ mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_local_variables(mrb_state *mrb, mrb_value self)
{
- struct RProc *proc;
- mrb_irep *irep;
- mrb_value vars;
- size_t i;
+ return mrb_proc_local_variables(mrb, mrb->c->ci[-1].proc);
+}
- proc = mrb->c->ci[-1].proc;
+KHASH_DECLARE(st, mrb_sym, char, FALSE)
+KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal)
- 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;
- }
- }
+struct mt_set {
+ khash_t(st) *set;
+ khash_t(st) *undef;
+};
+
+static int
+method_entry_i(mrb_state *mrb, mrb_sym mid, mrb_method_t m, void *p)
+{
+ struct mt_set *s = (struct mt_set*)p;
+
+ if (MRB_METHOD_UNDEF_P(m)) {
+ if (s->undef) {
+ kh_put(st, mrb, s->undef, mid);
}
- 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);
+ else if (s->undef == NULL ||
+ kh_get(st, mrb, s->undef, mid) == kh_end(s->undef)) {
+ kh_put(st, mrb, s->set, mid);
+ }
+ return 0;
}
-KHASH_DECLARE(st, mrb_sym, char, FALSE)
-
static void
-method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
+method_entry_loop(mrb_state *mrb, struct RClass *klass, khash_t(st) *set, khash_t(st) *undef)
{
- khint_t i;
+ struct mt_set s;
- khash_t(mt) *h = klass->mt;
- if (!h || kh_size(h) == 0) return;
- for (i=0;i<kh_end(h);i++) {
- if (kh_exist(h, i)) {
- mrb_method_t m = kh_value(h, i);
- if (MRB_METHOD_UNDEF_P(m)) continue;
- kh_put(st, mrb, set, kh_key(h, i));
- }
- }
+ s.set = set;
+ s.undef = undef;
+ mrb_mt_foreach(mrb, klass, method_entry_i, (void*)&s);
}
-mrb_value
-mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj)
+static mrb_value
+mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass *klass)
{
- khint_t i;
mrb_value ary;
- mrb_bool prepended = FALSE;
- struct RClass* oldklass;
- khash_t(st)* set = kh_init(st, mrb);
+ struct RClass *oldklass;
+ khash_t(st) *set = kh_init(st, mrb);
- if (!recur && (klass->flags & MRB_FL_CLASS_IS_PREPENDED)) {
- MRB_CLASS_ORIGIN(klass);
- prepended = TRUE;
+ if (!recur) {
+ if (klass->flags & MRB_FL_CLASS_IS_PREPENDED) {
+ MRB_CLASS_ORIGIN(klass);
+ }
+ method_entry_loop(mrb, klass, set, NULL);
}
+ else {
+ khash_t(st) *undef = kh_init(st, mrb);
- 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 = NULL;
+ while (klass && (klass != oldklass)) {
+ method_entry_loop(mrb, klass, set, undef);
+ oldklass = klass;
+ klass = klass->super;
}
- oldklass = klass;
- klass = klass->super;
+ kh_destroy(st, mrb, undef);
}
ary = mrb_ary_new_capa(mrb, kh_size(set));
- for (i=0;i<kh_end(set);i++) {
+ for (khint_t i=0; i<kh_end(set); i++) {
if (kh_exist(set, i)) {
mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
}
@@ -227,7 +212,7 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl
static mrb_value
mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
{
- return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
+ return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj));
}
/* 15.3.1.3.31 */
/*
@@ -312,18 +297,19 @@ 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);
+ struct RClass *klass;
+ khash_t(st) *set = kh_init(st, mrb);
+ khash_t(st) *undef = (recur ? kh_init(st, mrb) : NULL);
klass = mrb_class(mrb, obj);
if (klass && (klass->tt == MRB_TT_SCLASS)) {
- method_entry_loop(mrb, klass, set);
+ method_entry_loop(mrb, klass, set, undef);
klass = klass->super;
}
if (recur) {
while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) {
- method_entry_loop(mrb, klass, set);
+ method_entry_loop(mrb, klass, set, undef);
klass = klass->super;
}
}
@@ -335,6 +321,7 @@ mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
}
}
kh_destroy(st, mrb, set);
+ if (undef) kh_destroy(st, mrb, undef);
return ary;
}
@@ -380,41 +367,29 @@ mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self)
return mrb_obj_singleton_methods(mrb, recur, self);
}
+mrb_value mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c);
+
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);
+ return mrb_mod_define_method_m(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)));
}
-static void
-check_cv_name_str(mrb_state *mrb, mrb_value str)
+static mrb_bool
+cv_name_p(mrb_state *mrb, const char *name, mrb_int len)
{
- 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);
- }
+ return len > 2 && name[0] == '@' && name[1] == '@' &&
+ !ISDIGIT(name[2]) && mrb_ident_p(name+2, len-2);
}
static void
check_cv_name_sym(mrb_state *mrb, mrb_sym id)
{
- check_cv_name_str(mrb, mrb_sym2str(mrb, id));
+ mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, id, &len);
+ if (!cv_name_p(mrb, name, len)) {
+ mrb_name_error(mrb, id, "'%n' is not allowed as a class variable name", id);
+ }
}
/* 15.2.2.4.39 */
@@ -453,12 +428,10 @@ mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
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, "cannot remove %n for %v", id, mod);
}
- mrb_name_error(mrb, id, "class variable %S not defined for %S",
- mrb_sym2str(mrb, id), mod);
+ mrb_name_error(mrb, id, "class variable %n not defined for %v", id, mod);
/* not reached */
return mrb_nil_value();
@@ -565,8 +538,6 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
return result;
}
-mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int);
-
/* 15.2.2.4.33 */
/*
* call-seq:
@@ -601,30 +572,7 @@ 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);
+ return mrb_class_instance_method_list(mrb, recur, c);
}
/* 15.2.2.4.41 */
@@ -640,11 +588,14 @@ static mrb_value
mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
{
mrb_int argc;
- mrb_value *argv;
+ const mrb_value *argv;
+ struct RClass *c = mrb_class_ptr(mod);
mrb_get_args(mrb, "*", &argv, &argc);
+ mrb_check_frozen(mrb, mrb_obj_ptr(mod));
while (argc--) {
- remove_method(mrb, mod, mrb_obj_to_sym(mrb, *argv));
+ mrb_remove_method(mrb, c, mrb_obj_to_sym(mrb, *argv));
+ mrb_funcall_id(mrb, mod, MRB_SYM(method_removed), 1, *argv);
argv++;
}
return mod;
@@ -657,8 +608,31 @@ mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
return mrb_nil_value(); /* not reached */
}
-/* implementation of Module.nesting */
-mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value);
+static mrb_value
+mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod)
+{
+ const struct RProc *proc;
+ mrb_value ary;
+ struct RClass *c = NULL;
+
+ ary = mrb_ary_new(mrb);
+ proc = mrb->c->ci[-1].proc; /* callee proc */
+ while (proc && !MRB_PROC_CFUNC_P(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;
+}
+
+/* implementation of #send method */
+mrb_value mrb_f_send(mrb_state *mrb, mrb_value self);
void
mrb_mruby_metaprog_gem_init(mrb_state* mrb)
@@ -666,8 +640,8 @@ 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, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 (15.3.1.2.4) */
+ mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 (15.3.1.2.7) */
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 */
@@ -679,10 +653,10 @@ mrb_mruby_metaprog_gem_init(mrb_state* mrb)
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, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_REQ(1)|MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, krn, "send", mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 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, "class_variables", mrb_mod_class_variables, MRB_ARGS_OPT(1)); /* 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 */
@@ -693,7 +667,7 @@ mrb_mruby_metaprog_gem_init(mrb_state* mrb)
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 */
+ mrb_define_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_NONE()); /* 15.2.2.3.2 */
}
void
diff --git a/mrbgems/mruby-metaprog/test/metaprog.rb b/mrbgems/mruby-metaprog/test/metaprog.rb
index 748fe9c6c..4d2651702 100644
--- a/mrbgems/mruby-metaprog/test/metaprog.rb
+++ b/mrbgems/mruby-metaprog/test/metaprog.rb
@@ -1,17 +1,3 @@
-assert('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
@@ -34,6 +20,32 @@ assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do
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])
+ assert_raise(NameError) { o.instance_variable_defined?("@0") }
+end
+
+assert('Kernel#instance_variable_get', '15.3.1.3.21') do
+ o = Class.new { attr_accessor :foo, :bar }.new
+ o.foo = "one"
+ o.bar = 2
+ assert_equal("one", o.instance_variable_get(:@foo))
+ assert_equal(2, o.instance_variable_get("@bar"))
+ assert_equal(nil, o.instance_variable_get(:@baz))
+ %w[foo @1].each do |n|
+ assert_raise(NameError) { o.instance_variable_get(n) }
+ end
+end
+
+assert('Kernel#instance_variable_set', '15.3.1.3.22') do
+ o = Class.new { attr_reader :foo, :_bar }.new
+ assert_equal("one", o.instance_variable_set(:@foo, "one"))
+ assert_equal("one", o.foo)
+ assert_equal(2, o.instance_variable_set("@_bar", 2))
+ assert_equal(2, o._bar)
+ %w[@6 @% @@a @ a].each do |n|
+ assert_raise(NameError) { o.instance_variable_set(n, 1) }
+ end
+ assert_raise(FrozenError) { o.freeze.instance_variable_set(:@a, 2) }
+ assert_raise(FrozenError, ArgumentError) { nil.instance_variable_set(:@a, 2) }
end
assert('Kernel#instance_variables', '15.3.1.3.23') do
@@ -52,6 +64,8 @@ end
assert('Kernel#methods', '15.3.1.3.31') do
assert_equal Array, methods.class
+ assert_equal [:foo], Class.new{def self.foo; end}.methods(false)
+ assert_equal [], Class.new{}.methods(false)
end
assert('Kernel#private_methods', '15.3.1.3.36') do
@@ -75,6 +89,32 @@ assert('Kernel#singleton_methods', '15.3.1.3.45') do
assert_equal singleton_methods.class, Array
end
+assert('Kernel.global_variables', '15.3.1.2.4') do
+ assert_equal Array, Kernel.global_variables.class
+end
+
+assert('Kernel#global_variables', '15.3.1.3.14') do
+ variables1 = global_variables
+ assert_equal Array, variables1.class
+ assert_not_include(variables1, :$kernel_global_variables_test)
+
+ $kernel_global_variables_test = nil
+ variables2 = global_variables
+ assert_include(variables2, :$kernel_global_variables_test)
+ assert_equal(1, variables2.size - variables1.size)
+end
+
+assert('Kernel#local_variables', '15.3.1.3.28') do
+ assert_equal Array, local_variables.class
+
+ def local_var_list
+ a = "hello"
+ local_variables
+ end
+
+ assert_equal [:a], local_var_list
+end
+
assert('Kernel.local_variables', '15.3.1.2.7') do
a, b = 0, 1
a += b
@@ -87,6 +127,15 @@ assert('Kernel.local_variables', '15.3.1.2.7') do
# Kernel#local_variables: 15.3.1.3.28
local_variables.sort
}.call(-1, -2)
+
+ a = Object.new
+ def a.hoge(vars, *, **)
+ Proc.new {
+ x, y = 1, 2
+ local_variables.sort
+ }
+ end
+ assert_equal([:vars, :x, :y]) { a.hoge(0).call }
end
assert('Kernel#define_singleton_method') do
@@ -96,6 +145,24 @@ assert('Kernel#define_singleton_method') do
end
assert_equal :test_method, ret
assert_equal :singleton_method_ok, o.test_method
+ assert_raise(TypeError) { 2.define_singleton_method(:f){} }
+ assert_raise(FrozenError) { [].freeze.define_singleton_method(:f){} }
+end
+
+assert('Kernel#singleton_class') do
+ o1 = Object.new
+ assert_same(o1.singleton_class, class << o1; self end)
+
+ o2 = Object.new
+ sc2 = class << o2; self end
+ assert_same(o2.singleton_class, sc2)
+
+ o3 = Object.new
+ sc3 = o3.singleton_class
+ o3.freeze
+ assert_predicate(sc3, :frozen?)
+
+ assert_predicate(Object.new.freeze.singleton_class, :frozen?)
end
def labeled_module(name, &block)
@@ -125,6 +192,7 @@ assert('Module#class_variable_defined?', '15.2.2.4.16') do
assert_true Test4ClassVariableDefined.class_variable_defined?(:@@cv)
assert_false Test4ClassVariableDefined.class_variable_defined?(:@@noexisting)
+ assert_raise(NameError) { Test4ClassVariableDefined.class_variable_defined?("@@2") }
end
assert('Module#class_variable_get', '15.2.2.4.17') do
@@ -133,6 +201,10 @@ assert('Module#class_variable_get', '15.2.2.4.17') do
end
assert_equal 99, Test4ClassVariableGet.class_variable_get(:@@cv)
+ assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(:@@a) }
+ %w[@@a? @@! @a a].each do |n|
+ assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(n) }
+ end
end
assert('Module#class_variable_set', '15.2.2.4.18') do
@@ -142,12 +214,21 @@ assert('Module#class_variable_set', '15.2.2.4.18') do
@@foo
end
end
-
- assert_true Test4ClassVariableSet.class_variable_set(:@@cv, 99)
- assert_true Test4ClassVariableSet.class_variable_set(:@@foo, 101)
+ assert_equal 99, Test4ClassVariableSet.class_variable_set(:@@cv, 99)
+ assert_equal 101, Test4ClassVariableSet.class_variable_set(:@@foo, 101)
assert_true Test4ClassVariableSet.class_variables.include? :@@cv
assert_equal 99, Test4ClassVariableSet.class_variable_get(:@@cv)
assert_equal 101, Test4ClassVariableSet.new.foo
+ %w[@@ @@1 @@x= @x @ x 1].each do |n|
+ assert_raise(NameError) { Test4ClassVariableSet.class_variable_set(n, 1) }
+ end
+
+ m = Module.new.freeze
+ assert_raise(FrozenError) { m.class_variable_set(:@@cv, 1) }
+
+ parent = Class.new{ class_variable_set(:@@a, nil) }.freeze
+ child = Class.new(parent)
+ assert_raise(FrozenError) { child.class_variable_set(:@@a, 1) }
end
assert('Module#class_variables', '15.2.2.4.19') do
@@ -223,6 +304,15 @@ assert('Module#remove_class_variable', '15.2.2.4.39') do
assert_equal 99, Test4RemoveClassVariable.remove_class_variable(:@@cv)
assert_false Test4RemoveClassVariable.class_variables.include? :@@cv
+ assert_raise(NameError) do
+ Test4RemoveClassVariable.remove_class_variable(:@@cv)
+ end
+ assert_raise(NameError) do
+ Test4RemoveClassVariable.remove_class_variable(:@v)
+ end
+ assert_raise(FrozenError) do
+ Test4RemoveClassVariable.freeze.remove_class_variable(:@@cv)
+ end
end
assert('Module#remove_method', '15.2.2.4.41') do
@@ -230,17 +320,19 @@ assert('Module#remove_method', '15.2.2.4.41') do
class Parent
def hello
end
- end
+ end
- class Child < Parent
+ class Child < Parent
def hello
end
end
end
- assert_true Test4RemoveMethod::Child.class_eval{ remove_method :hello }
- assert_true Test4RemoveMethod::Child.instance_methods.include? :hello
- assert_false Test4RemoveMethod::Child.instance_methods(false).include? :hello
+ klass = Test4RemoveMethod::Child
+ assert_same klass, klass.class_eval{ remove_method :hello }
+ assert_true klass.instance_methods.include? :hello
+ assert_false klass.instance_methods(false).include? :hello
+ assert_raise(FrozenError) { klass.freeze.remove_method :m }
end
assert('Module.nesting', '15.2.2.2.2') do
@@ -316,15 +408,15 @@ end
assert('alias_method and remove_method') do
begin
- Fixnum.alias_method :to_s_, :to_s
- Fixnum.remove_method :to_s
+ Integer.alias_method :to_s_, :to_s
+ Integer.remove_method :to_s
assert_nothing_raised do
# segfaults if mrb_cptr is used
1.to_s
end
ensure
- Fixnum.alias_method :to_s, :to_s_
- Fixnum.remove_method :to_s_
+ Integer.alias_method :to_s, :to_s_
+ Integer.remove_method :to_s_
end
end
diff --git a/mrbgems/mruby-method/README.md b/mrbgems/mruby-method/README.md
index 5076bfbb2..41130bb82 100644
--- a/mrbgems/mruby-method/README.md
+++ b/mrbgems/mruby-method/README.md
@@ -1,7 +1,6 @@
-mruby-method
-===
+# mruby-method
-A implementetion of class **Method** and **UnboundMethod** for mruby
+An implementation of class **Method** and **UnboundMethod** for mruby
```ruby
p Enumerable.instance_method(:find_all).source_location
@@ -10,11 +9,12 @@ p Enumerable.instance_method(:find_all).source_location
# Note
-`source_location` method need this configuration in build_config.rb
+You need to enable debug option in your build configuration to use
+`source_location` method in this gem, for example:
```ruby
MRuby::Build.new do |conf|
- enable_debug
+ conf.enable_debug
end
```
@@ -22,38 +22,38 @@ end
## Kernel
-- `Kernel#method`
-- `Kernel#singleton_method`
+* `Kernel#method`
+* `Kernel#singleton_method`
## Module
-- `Module#instance_method`
+* `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`
+* `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`
+* `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
+* <https://ruby-doc.org/core-2.3.3/Method.html>
+* <https://ruby-doc.org/core-2.3.3/UnboundMethod.html>
diff --git a/mrbgems/mruby-method/mrblib/kernel.rb b/mrbgems/mruby-method/mrblib/kernel.rb
index 2efc93f37..eb17df5a6 100644
--- a/mrbgems/mruby-method/mrblib/kernel.rb
+++ b/mrbgems/mruby-method/mrblib/kernel.rb
@@ -3,7 +3,7 @@ module Kernel
m = method(name)
sc = (class <<self; self; end)
if m.owner != sc
- raise NameError, "undefined method `#{name}' for class `#{sc}'"
+ raise NameError, "undefined method '#{name}' for class '#{sc}'"
end
m
end
diff --git a/mrbgems/mruby-method/mrblib/method.rb b/mrbgems/mruby-method/mrblib/method.rb
index 5de0afdf7..56af7cf61 100644
--- a/mrbgems/mruby-method/mrblib/method.rb
+++ b/mrbgems/mruby-method/mrblib/method.rb
@@ -6,15 +6,11 @@ class Method
}
end
- def owner
- @owner
+ def <<(other)
+ ->(*args, &block) { call(other.call(*args, &block)) }
end
- def receiver
- @recv
- end
-
- def name
- @name
+ def >>(other)
+ ->(*args, &block) { other.call(call(*args, &block)) }
end
end
diff --git a/mrbgems/mruby-method/mrblib/unbound_method.rb b/mrbgems/mruby-method/mrblib/unbound_method.rb
deleted file mode 100644
index 1d3acf3fa..000000000
--- a/mrbgems/mruby-method/mrblib/unbound_method.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class UnboundMethod
- def owner
- @owner
- end
-
- def name
- @name
- end
-end
diff --git a/mrbgems/mruby-method/src/method.c b/mrbgems/mruby-method/src/method.c
index 600567cac..02131050d 100644
--- a/mrbgems/mruby-method/src/method.c
+++ b/mrbgems/mruby-method/src/method.c
@@ -4,76 +4,194 @@
#include "mruby/variable.h"
#include "mruby/proc.h"
#include "mruby/string.h"
+#include "mruby/presym.h"
+
+mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
+
+static mrb_value
+args_shift(mrb_state *mrb)
+{
+ mrb_value *argv = mrb->c->ci->stack + 1;
+
+ if (mrb->c->ci->argc > 0) {
+ mrb_value obj = argv[0];
+ memmove(argv, argv + 1, (mrb->c->ci->argc + 1 /* block */ - 1 /* first value */) * sizeof(mrb_value));
+ mrb->c->ci->argc--;
+ return obj;
+ }
+ else if (mrb->c->ci->argc < 0 && RARRAY_LEN(*argv) > 0) {
+ return mrb_ary_shift(mrb, *argv);
+ }
+ else {
+ mrb_argnum_error(mrb, 0, 1, -1);
+ return mrb_undef_value(); /* not reached */
+ }
+}
+
+static void
+args_unshift(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value *argv = mrb->c->ci->stack + 1;
+
+ if (mrb->c->ci->argc >= 0) {
+ mrb_value block = argv[mrb->c->ci->argc];
+ argv[0] = mrb_ary_new_from_values(mrb, mrb->c->ci->argc, argv);
+ argv[1] = block;
+ mrb->c->ci->argc = -1;
+ }
+
+ mrb_ary_unshift(mrb, *argv, obj);
+}
+
+static struct RProc*
+method_missing_prepare(mrb_state *mrb, mrb_sym *mid, mrb_value recv, struct RClass **tc)
+{
+ const mrb_sym id_method_missing = MRB_SYM(method_missing);
+
+ if (*mid == id_method_missing) {
+ method_missing: ;
+ int argc = mrb->c->ci->argc;
+ mrb_value *argv = mrb->c->ci->stack + 1;
+ mrb_value args = (argc < 0) ? argv[0] : mrb_ary_new_from_values(mrb, argc, argv);
+ mrb_method_missing(mrb, *mid, recv, args);
+ }
+
+ *tc = mrb_class(mrb, recv);
+ mrb_method_t m = mrb_method_search_vm(mrb, tc, id_method_missing);
+ if (MRB_METHOD_UNDEF_P(m)) {
+ goto method_missing;
+ }
+
+ struct RProc *proc;
+ if (MRB_METHOD_FUNC_P(m)) {
+ proc = mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
+ MRB_PROC_SET_TARGET_CLASS(proc, *tc);
+ }
+ else {
+ proc = MRB_METHOD_PROC(m);
+ }
+
+ args_unshift(mrb, mrb_symbol_value(*mid));
+ *mid = id_method_missing;
+
+ return proc;
+}
static struct RObject *
method_object_alloc(mrb_state *mrb, struct RClass *mclass)
{
- return (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mclass);
+ return MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mclass);
+}
+
+static struct RProc*
+method_extract_proc(mrb_state *mrb, mrb_value self)
+{
+ mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_proc));
+ if (mrb_nil_p(obj)) {
+ return NULL;
+ }
+ else {
+ mrb_check_type(mrb, obj, MRB_TT_PROC);
+ return mrb_proc_ptr(obj);
+ }
}
static mrb_value
-unbound_method_bind(mrb_state *mrb, mrb_value self)
+method_extract_receiver(mrb_state *mrb, mrb_value self)
{
- struct RObject *me;
- 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"));
- mrb_value recv;
+ return mrb_iv_get(mrb, self, MRB_SYM(_recv));
+}
+
+static mrb_sym
+method_extract_mid(mrb_state *mrb, mrb_value self)
+{
+ mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_name));
+ mrb_check_type(mrb, obj, MRB_TT_SYMBOL);
+ return mrb_symbol(obj);
+}
- mrb_get_args(mrb, "o", &recv);
+static struct RClass*
+method_extract_owner(mrb_state *mrb, mrb_value self)
+{
+ mrb_value obj = mrb_iv_get(mrb, self, MRB_SYM(_owner));
+ switch (mrb_type(obj)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ case MRB_TT_SCLASS:
+ break;
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "not class/module as owner of method object");
+ }
+ return mrb_class_ptr(obj);
+}
- if (mrb_type(owner) != MRB_TT_MODULE &&
+static void
+bind_check(mrb_state *mrb, mrb_value recv, mrb_value owner)
+{
+ if (!mrb_module_p(owner) &&
mrb_class_ptr(owner) != mrb_obj_class(mrb, recv) &&
!mrb_obj_is_kind_of(mrb, recv, mrb_class_ptr(owner))) {
- if (mrb_type(owner) == MRB_TT_SCLASS) {
- mrb_raise(mrb, E_TYPE_ERROR, "singleton method called for a different object");
- } else {
- const char *s = mrb_class_name(mrb, mrb_class_ptr(owner));
- mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %S", mrb_str_new_static(mrb, s, strlen(s)));
- }
+ if (mrb_sclass_p(owner)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "singleton method called for a different object");
+ } else {
+ mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %v", owner);
+ }
}
- me = method_object_alloc(mrb, mrb_class_get(mrb, "Method"));
- mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), owner);
- 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"), proc);
- mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), klass);
+}
+
+static mrb_value
+unbound_method_bind(mrb_state *mrb, mrb_value self)
+{
+ struct RObject *me;
+ mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner));
+ mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name));
+ mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc));
+ mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass));
+ mrb_value recv = mrb_get_arg1(mrb);
+
+ bind_check(mrb, recv, owner);
+ me = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(Method)));
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), owner);
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), recv);
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_name), name);
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), proc);
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), klass);
return mrb_obj_value(me);
}
-#define IV_GET(value, name) mrb_iv_get(mrb, value, mrb_intern_lit(mrb, name))
+#define IV_GET(value, name) mrb_iv_get(mrb, value, name)
static mrb_value
method_eql(mrb_state *mrb, mrb_value self)
{
- mrb_value other, receiver, orig_proc, other_proc;
+ mrb_value other = mrb_get_arg1(mrb);
+ mrb_value receiver, orig_proc, other_proc;
struct RClass *owner, *klass;
struct RProc *orig_rproc, *other_rproc;
- mrb_get_args(mrb, "o", &other);
if (!mrb_obj_is_instance_of(mrb, other, mrb_class(mrb, self)))
return mrb_false_value();
if (mrb_class(mrb, self) != mrb_class(mrb, other))
return mrb_false_value();
- klass = mrb_class_ptr(IV_GET(self, "@klass"));
- if (klass != mrb_class_ptr(IV_GET(other, "@klass")))
+ klass = mrb_class_ptr(IV_GET(self, MRB_SYM(_klass)));
+ if (klass != mrb_class_ptr(IV_GET(other, MRB_SYM(_klass))))
return mrb_false_value();
- owner = mrb_class_ptr(IV_GET(self, "@owner"));
- if (owner != mrb_class_ptr(IV_GET(other, "@owner")))
+ owner = mrb_class_ptr(IV_GET(self, MRB_SYM(_owner)));
+ if (owner != mrb_class_ptr(IV_GET(other, MRB_SYM(_owner))))
return mrb_false_value();
- receiver = IV_GET(self, "@recv");
- if (!mrb_obj_equal(mrb, receiver, IV_GET(other, "@recv")))
+ receiver = IV_GET(self, MRB_SYM(_recv));
+ if (!mrb_obj_equal(mrb, receiver, IV_GET(other, MRB_SYM(_recv))))
return mrb_false_value();
- orig_proc = IV_GET(self, "proc");
- other_proc = IV_GET(other, "proc");
+ orig_proc = IV_GET(self, MRB_SYM(_proc));
+ other_proc = IV_GET(other, MRB_SYM(_proc));
if (mrb_nil_p(orig_proc) && mrb_nil_p(other_proc)) {
- if (mrb_symbol(IV_GET(self, "@name")) == mrb_symbol(IV_GET(other, "@name")))
+ if (mrb_symbol(IV_GET(self, MRB_SYM(_name))) == mrb_symbol(IV_GET(other, MRB_SYM(_name))))
return mrb_true_value();
else
return mrb_false_value();
@@ -105,53 +223,57 @@ method_eql(mrb_state *mrb, mrb_value self)
#undef IV_GET
static mrb_value
-method_call(mrb_state *mrb, mrb_value self)
+mcall(mrb_state *mrb, mrb_value self, mrb_value recv)
{
- 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);
+ struct RProc *proc = method_extract_proc(mrb, self);
+ mrb_sym mid = method_extract_mid(mrb, self);
+ struct RClass *tc = method_extract_owner(mrb, self);
+
+ if (mrb_undef_p(recv)) {
+ recv = method_extract_receiver(mrb, self);
}
else {
- ret = mrb_yield_with_class(mrb, proc, argc, argv, recv, owner);
+ bind_check(mrb, recv, mrb_obj_value(tc));
}
- mrb->c->ci->mid = orig_mid;
- return ret;
+
+ if (!proc) {
+ proc = method_missing_prepare(mrb, &mid, recv, &tc);
+ }
+ mrb->c->ci->mid = mid;
+ mrb->c->ci->u.target_class = tc;
+
+ return mrb_exec_irep(mrb, recv, proc);
+}
+
+static mrb_value
+method_call(mrb_state *mrb, mrb_value self)
+{
+ return mcall(mrb, self, mrb_undef_value());
+}
+
+static mrb_value
+method_bcall(mrb_state *mrb, mrb_value self)
+{
+ mrb_value recv = args_shift(mrb);
+ mrb_gc_protect(mrb, recv);
+ return mcall(mrb, self, recv);
}
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);
+ mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner));
+ mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name));
+ mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc));
+ mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass));
+
+ ume = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(UnboundMethod)));
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_owner), owner);
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_recv), mrb_nil_value());
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_name), name);
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_proc), proc);
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_klass), klass);
return mrb_obj_value(ume);
}
@@ -170,10 +292,10 @@ method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
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"));
+ mrb_value recv = mrb_iv_get(mrb, self, MRB_SYM(_recv));
+ mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass));
+ mrb_value owner = mrb_iv_get(mrb, self, MRB_SYM(_owner));
+ mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name));
struct RClass *super, *rklass;
struct RProc *proc;
struct RObject *me;
@@ -199,11 +321,11 @@ method_super_method(mrb_state *mrb, mrb_value self)
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));
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), mrb_obj_value(super));
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), recv);
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_name), name);
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), mrb_obj_value(proc));
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), mrb_obj_value(rklass));
return mrb_obj_value(me);
}
@@ -211,26 +333,15 @@ method_super_method(mrb_state *mrb, mrb_value self)
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;
+ mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc));
+ mrb_int arity = mrb_nil_p(proc) ? -1 : mrb_proc_arity(mrb_proc_ptr(proc));
+ return mrb_fixnum_value(arity);
}
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"));
+ mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_proc));
struct RProc *rproc;
struct RClass *orig;
mrb_value ret;
@@ -241,7 +352,7 @@ method_source_location(mrb_state *mrb, mrb_value self)
rproc = mrb_proc_ptr(proc);
orig = rproc->c;
rproc->c = mrb->proc_class;
- ret = mrb_funcall(mrb, proc, "source_location", 0);
+ ret = mrb_funcall_id(mrb, proc, MRB_SYM(source_location), 0);
rproc->c = orig;
return ret;
}
@@ -249,13 +360,13 @@ method_source_location(mrb_state *mrb, mrb_value self)
static mrb_value
method_parameters(mrb_state *mrb, mrb_value self)
{
- mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
+ mrb_value proc = mrb_iv_get(mrb, self, MRB_SYM(_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 rest = mrb_symbol_value(MRB_SYM(rest));
mrb_value arest = mrb_ary_new_from_values(mrb, 1, &rest);
return mrb_ary_new_from_values(mrb, 1, &arest);
}
@@ -263,7 +374,7 @@ method_parameters(mrb_state *mrb, mrb_value self)
rproc = mrb_proc_ptr(proc);
orig = rproc->c;
rproc->c = mrb->proc_class;
- ret = mrb_funcall(mrb, proc, "parameters", 0);
+ ret = mrb_funcall_id(mrb, proc, MRB_SYM(parameters), 0);
rproc->c = orig;
return ret;
}
@@ -271,9 +382,9 @@ method_parameters(mrb_state *mrb, mrb_value self)
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 owner = mrb_iv_get(mrb, self, MRB_SYM(_owner));
+ mrb_value klass = mrb_iv_get(mrb, self, MRB_SYM(_klass));
+ mrb_value name = mrb_iv_get(mrb, self, MRB_SYM(_name));
mrb_value str = mrb_str_new_lit(mrb, "#<");
struct RClass *rklass;
@@ -281,16 +392,16 @@ method_to_s(mrb_state *mrb, mrb_value 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_concat(mrb, str, owner);
mrb_str_cat_lit(mrb, str, "#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_concat(mrb, str, name);
}
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_concat(mrb, str, owner);
mrb_str_cat_lit(mrb, str, ")#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_concat(mrb, str, name);
}
mrb_str_cat_lit(mrb, str, ">");
return str;
@@ -300,7 +411,6 @@ 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);
@@ -308,10 +418,10 @@ mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym
if (unbound) {
goto name_error;
}
- if (!mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "respond_to_missing?"))) {
+ if (!mrb_respond_to(mrb, obj, MRB_SYM_Q(respond_to_missing))) {
goto name_error;
}
- ret = mrb_funcall(mrb, obj, "respond_to_missing?", 2, mrb_symbol_value(name), mrb_true_value());
+ ret = mrb_funcall_id(mrb, obj, MRB_SYM_Q(respond_to_missing), 2, mrb_symbol_value(name), mrb_true_value());
if (!mrb_test(ret)) {
goto name_error;
}
@@ -324,13 +434,7 @@ mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym
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))
- );
+ mrb_raisef(mrb, E_NAME_ERROR, "undefined method '%n' for class '%C'", name, c);
}
static mrb_value
@@ -345,12 +449,12 @@ mrb_kernel_method(mrb_state *mrb, mrb_value self)
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)));
+ me = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(Method)));
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_owner), mrb_obj_value(owner));
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_recv), self);
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_name), mrb_symbol_value(name));
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_proc), proc ? mrb_obj_value(proc) : mrb_nil_value());
+ mrb_obj_iv_set(mrb, me, MRB_SYM(_klass), mrb_obj_value(mrb_class(mrb, self)));
return mrb_obj_value(me);
}
@@ -367,45 +471,69 @@ mrb_module_instance_method(mrb_state *mrb, mrb_value self)
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);
+ ume = method_object_alloc(mrb, mrb_class_get_id(mrb, MRB_SYM(UnboundMethod)));
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_owner), mrb_obj_value(owner));
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_recv), mrb_nil_value());
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_name), mrb_symbol_value(name));
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_proc), proc ? mrb_obj_value(proc) : mrb_nil_value());
+ mrb_obj_iv_set(mrb, ume, MRB_SYM(_klass), self);
return mrb_obj_value(ume);
}
+static mrb_value
+method_owner(mrb_state *mrb, mrb_value self)
+{
+ return mrb_iv_get(mrb, self, MRB_SYM(_owner));
+}
+
+static mrb_value
+method_receiver(mrb_state *mrb, mrb_value self)
+{
+ return mrb_iv_get(mrb, self, MRB_SYM(_recv));
+}
+
+static mrb_value
+method_name(mrb_state *mrb, mrb_value self)
+{
+ return mrb_iv_get(mrb, self, MRB_SYM(_name));
+}
+
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);
+ struct RClass *unbound_method = mrb_define_class_id(mrb, MRB_SYM(UnboundMethod), mrb->object_class);
+ struct RClass *method = mrb_define_class_id(mrb, MRB_SYM(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, "eql?", method_eql, MRB_ARGS_REQ(1));
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_define_method(mrb, unbound_method, "bind_call", method_bcall, MRB_ARGS_REQ(1)|MRB_ARGS_ANY());
+ mrb_define_method(mrb, unbound_method, "owner", method_owner, MRB_ARGS_NONE());
+ mrb_define_method(mrb, unbound_method, "name", method_name, 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, "eql?", method_eql, MRB_ARGS_REQ(1));
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, "[]", method_call, MRB_ARGS_ANY());
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, method, "owner", method_owner, MRB_ARGS_NONE());
+ mrb_define_method(mrb, method, "receiver", method_receiver, MRB_ARGS_NONE());
+ mrb_define_method(mrb, method, "name", method_name, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->kernel_module, "method", mrb_kernel_method, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-method/test/method.rb b/mrbgems/mruby-method/test/method.rb
index 9fd6a558e..675277726 100644
--- a/mrbgems/mruby-method/test/method.rb
+++ b/mrbgems/mruby-method/test/method.rb
@@ -21,7 +21,7 @@ class Interpreter
}
def interpret(string)
@ret = ""
- string.each_char {|b| Dispatcher[b].bind(self).call }
+ string.split("").each {|b| Dispatcher[b].bind(self).call }
end
end
@@ -77,7 +77,7 @@ end
assert 'instance' do
assert_kind_of Method, 1.method(:+)
- assert_kind_of UnboundMethod, Fixnum.instance_method(:+)
+ assert_kind_of UnboundMethod, Integer.instance_method(:+)
end
assert 'Method#call' do
@@ -92,9 +92,9 @@ assert 'Method#call' do
assert_equal 42, klass2.new.method(:foo).call
i = Class.new {
- def bar
- yield 3
- end
+ def bar
+ yield 3
+ end
}.new
assert_raise(LocalJumpError) { i.method(:bar).call }
assert_equal 3, i.method(:bar).call { |i| i }
@@ -371,6 +371,25 @@ assert "Method#initialize_copy" do
assert_equal(m1, m2)
end
+assert "Method#<< and Method#>>" do
+ obj = Object.new
+ class << obj
+ def mul2(n); n * 2; end
+ def add3(n); n + 3; end
+ end
+
+ f = obj.method(:mul2)
+ g = obj.method(:add3)
+
+ m1 = f << g
+ assert_kind_of Proc, m1
+ assert_equal 16, m1.call(5)
+
+ m2 = f >> g
+ assert_kind_of Proc, m2
+ assert_equal 13, m2.call(5)
+end
+
assert 'UnboundMethod#arity' do
c = Class.new {
def foo(a, b)
@@ -385,10 +404,11 @@ assert 'UnboundMethod#arity' do
end
assert 'UnboundMethod#==' do
- assert_false(Fixnum.instance_method(:+) == Fixnum.instance_method(:-))
- assert_true(Fixnum.instance_method(:+) == Fixnum.instance_method(:+))
- assert_false(Fixnum.instance_method(:+) == Float.instance_method(:+))
+ assert_false(Integer.instance_method(:+) == Integer.instance_method(:-))
+ assert_true(Integer.instance_method(:+) == Integer.instance_method(:+))
assert_true(UnboundMethod.instance_method(:==) == UnboundMethod.instance_method(:eql?))
+ skip unless Object.const_defined?(:Float)
+ assert_false(Integer.instance_method(:+) == Float.instance_method(:+))
end
assert 'UnboundMethod#super_method' do
@@ -422,3 +442,11 @@ assert 'UnboundMethod#bind' do
assert_raise(TypeError) { Array.instance_method(:each).bind(1) }
assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new)
end
+
+assert 'UnboundMethod#bind_call' do
+ m = Array.instance_method(:size)
+ assert_equal(:size, m.name)
+ assert_equal(0, m.bind_call([]))
+ assert_equal(1, m.bind_call([1]))
+ assert_equal(2, m.bind_call([1,2]))
+end
diff --git a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
index f250538fe..576605cb1 100644
--- a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
+++ b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
@@ -1,8 +1,4 @@
-module Integral
- def div(other)
- self.divmod(other)[0]
- end
-
+class Numeric
def zero?
self == 0
end
diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
index 1d6a07769..00b13174f 100644
--- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c
+++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
@@ -1,41 +1,7 @@
#include <limits.h>
#include <mruby.h>
-
-static inline mrb_int
-to_int(mrb_value x)
-{
- double f;
-
- if (mrb_fixnum_p(x)) return mrb_fixnum(x);
- f = mrb_float(x);
- return (mrb_int)f;
-}
-
-/*
- * 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(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);
-}
+#include <mruby/numeric.h>
+#include <mruby/presym.h>
/*
* call-seq:
@@ -48,8 +14,8 @@ mrb_int_allbits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = mrb_as_int(mrb, self);
return mrb_bool_value((n & m) == m);
}
@@ -64,8 +30,8 @@ mrb_int_anybits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = mrb_as_int(mrb, self);
return mrb_bool_value((n & m) != 0);
}
@@ -80,20 +46,32 @@ mrb_int_nobits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = mrb_as_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");
+ struct RClass *i = mrb_class_get(mrb, "Integer");
- 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));
+
+#ifndef MRB_NO_FLOAT
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(RADIX), mrb_fixnum_value(MRB_FLT_RADIX));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(MANT_DIG), mrb_fixnum_value(MRB_FLT_MANT_DIG));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(EPSILON), mrb_float_value(mrb, MRB_FLT_EPSILON));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(DIG), mrb_fixnum_value(MRB_FLT_DIG));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(MIN_EXP), mrb_fixnum_value(MRB_FLT_MIN_EXP));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(MIN), mrb_float_value(mrb, MRB_FLT_MIN));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(MIN_10_EXP), mrb_fixnum_value(MRB_FLT_MIN_10_EXP));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(MAX_EXP), mrb_fixnum_value(MRB_FLT_MAX_EXP));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(MAX), mrb_float_value(mrb, MRB_FLT_MAX));
+ mrb_define_const_id(mrb, mrb->float_class, MRB_SYM(MAX_10_EXP), mrb_fixnum_value(MRB_FLT_MAX_10_EXP));
+#endif /* MRB_NO_FLOAT */
}
void
diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb
index 6ea0c14e7..efdf25a34 100644
--- a/mrbgems/mruby-numeric-ext/test/numeric.rb
+++ b/mrbgems/mruby-numeric-ext/test/numeric.rb
@@ -1,21 +1,14 @@
##
# 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
+ skip unless Object.const_defined?(:Float)
assert_float 52, 365.2425.div(7)
-end if class_defined?("Float")
+end
assert('Integer#zero?') do
assert_equal true, 0.zero?
diff --git a/mrbgems/mruby-object-ext/mrbgem.rake b/mrbgems/mruby-object-ext/mrbgem.rake
index 6d14b4a51..1aea2c8cd 100644
--- a/mrbgems/mruby-object-ext/mrbgem.rake
+++ b/mrbgems/mruby-object-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-object-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'Object class extension'
+ spec.summary = 'extensional methods shared by all objects'
end
diff --git a/mrbgems/mruby-object-ext/mrblib/object.rb b/mrbgems/mruby-object-ext/mrblib/object.rb
index 581156cb0..f014df469 100644
--- a/mrbgems/mruby-object-ext/mrblib/object.rb
+++ b/mrbgems/mruby-object-ext/mrblib/object.rb
@@ -1,4 +1,18 @@
-class Object
+module Kernel
+ # call-seq:
+ # obj.yield_self {|_obj|...} -> an_object
+ # obj.then {|_obj|...} -> an_object
+ #
+ # Yields <i>obj</i> 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
+
##
# call-seq:
# obj.tap{|x|...} -> obj
diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c
index b076b3ec0..a65feea6d 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -1,7 +1,9 @@
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
+#include <mruby/hash.h>
#include <mruby/proc.h>
+#include <mruby/presym.h>
/*
* call-seq:
@@ -16,7 +18,7 @@ nil_to_a(mrb_state *mrb, mrb_value obj)
return mrb_ary_new(mrb);
}
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
/*
* call-seq:
* nil.to_f -> 0.0
@@ -33,6 +35,19 @@ nil_to_f(mrb_state *mrb, mrb_value obj)
/*
* call-seq:
+ * nil.to_h -> {}
+ *
+ * Always returns an empty hash.
+ */
+
+static mrb_value
+nil_to_h(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_hash_new(mrb);
+}
+
+/*
+ * call-seq:
* nil.to_i -> 0
*
* Always returns zero.
@@ -46,6 +61,22 @@ nil_to_i(mrb_state *mrb, mrb_value obj)
/*
* call-seq:
+ * obj.itself -> an_object
+ *
+ * Returns <i>obj</i>.
+ *
+ * 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;
+}
+
+/*
+ * call-seq:
* obj.instance_exec(arg...) {|var...| block } -> obj
*
* Executes the given block within the context of the receiver
@@ -70,25 +101,12 @@ mrb_obj_instance_exec(mrb_state *mrb, mrb_value self)
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_get_args(mrb, "*&!", &argv, &argc, &blk);
+ c = mrb_singleton_class_ptr(mrb, self);
+ if (mrb->c->ci->acc < 0) {
+ return mrb_yield_with_class(mrb, blk, argc, argv, self, c);
}
- mrb->c->ci->target_class = c;
+ mrb_vm_ci_target_class_set(mrb->c->ci, c);
return mrb_yield_cont(mrb, blk, self, argc, argv);
}
@@ -98,12 +116,15 @@ 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
+#ifndef MRB_NO_FLOAT
mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE());
#endif
+ mrb_define_method(mrb, n, "to_h", nil_to_h, MRB_ARGS_NONE());
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());
+ mrb_define_method(mrb, mrb->kernel_module, "itself", mrb_f_itself, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, mrb_class_get_id(mrb, MRB_SYM(BasicObject)), "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
}
void
diff --git a/mrbgems/mruby-object-ext/test/nil.rb b/mrbgems/mruby-object-ext/test/nil.rb
index 7f773637a..9975e785a 100644
--- a/mrbgems/mruby-object-ext/test/nil.rb
+++ b/mrbgems/mruby-object-ext/test/nil.rb
@@ -3,8 +3,13 @@ assert('NilClass#to_a') do
end
assert('NilClass#to_f') do
+ skip unless Object.const_defined?(:Float)
assert_equal 0.0, nil.to_f
-end if class_defined?("Float")
+end
+
+assert('NilClass#to_h') do
+ assert_equal Hash.new, nil.to_h
+end
assert('NilClass#to_i') do
assert_equal 0, nil.to_i
diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
index b31dee04c..929a6180d 100644
--- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c
+++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
@@ -2,6 +2,15 @@
#include <mruby/gc.h>
#include <mruby/hash.h>
#include <mruby/class.h>
+#include <mruby/object.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/array.h>
+#include <mruby/variable.h>
+#include <mruby/proc.h>
+#include <mruby/value.h>
+#include <mruby/range.h>
+#include <mruby/presym.h>
struct os_count_struct {
mrb_int total;
@@ -63,19 +72,14 @@ os_count_objects(mrb_state *mrb, mrb_value self)
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));
+ mrb_hash_set(mrb, hash, mrb_symbol_value(MRB_SYM(TOTAL)), mrb_fixnum_value(obj_count.total));
+ mrb_hash_set(mrb, hash, mrb_symbol_value(MRB_SYM(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_INTEGER);
COUNT_TYPE(T_FLOAT);
COUNT_TYPE(T_CPTR);
COUNT_TYPE(T_OBJECT);
@@ -89,10 +93,12 @@ os_count_objects(mrb_state *mrb, mrb_value self)
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);
+ COUNT_TYPE(T_ISTRUCT);
+ COUNT_TYPE(T_COMPLEX);
+ COUNT_TYPE(T_RATIONAL);
#undef COUNT_TYPE
default:
type = mrb_fixnum_value(i); break;
@@ -105,9 +111,9 @@ os_count_objects(mrb_state *mrb, mrb_value self)
}
struct os_each_object_data {
- mrb_value block;
struct RClass *target_module;
mrb_int count;
+ mrb_value block;
};
static int
@@ -122,8 +128,8 @@ os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud)
/* filter internal objects */
switch (obj->tt) {
- case MRB_TT_ENV:
- case MRB_TT_ICLASS:
+ case MRB_TT_FREE: case MRB_TT_ENV:
+ case MRB_TT_BREAK: case MRB_TT_ICLASS:
return MRB_EACH_OBJ_OK;
default:
break;
@@ -159,16 +165,8 @@ os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud)
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;
+ struct os_each_object_data d = {0};
+ mrb_get_args(mrb, "&!|c", &d.block, &d.target_module);
mrb_objspace_each_objects(mrb, os_each_object_cb, &d);
return mrb_fixnum_value(d.count);
}
diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb
index 0553b97e2..9c44c2157 100644
--- a/mrbgems/mruby-objectspace/test/objectspace.rb
+++ b/mrbgems/mruby-objectspace/test/objectspace.rb
@@ -1,6 +1,6 @@
assert('ObjectSpace.count_objects') do
h = {}
- f = Fiber.new {} if Object.const_defined? :Fiber
+ 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) })
@@ -56,5 +56,5 @@ assert('ObjectSpace.each_object') do
end
assert 'Check class pointer of ObjectSpace.each_object.' do
- ObjectSpace.each_object { |obj| !obj }
+ assert_nothing_raised { ObjectSpace.each_object { |obj| !obj } }
end
diff --git a/mrbgems/mruby-os-memsize/mrbgem.rake b/mrbgems/mruby-os-memsize/mrbgem.rake
new file mode 100644
index 000000000..b5c163bba
--- /dev/null
+++ b/mrbgems/mruby-os-memsize/mrbgem.rake
@@ -0,0 +1,10 @@
+MRuby::Gem::Specification.new('mruby-os-memsize') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'ObjectSpace memsize_of method'
+
+ spec.add_dependency('mruby-objectspace')
+ spec.add_test_dependency('mruby-metaprog')
+ spec.add_test_dependency('mruby-method')
+ spec.add_test_dependency('mruby-fiber')
+end
diff --git a/mrbgems/mruby-os-memsize/src/memsize.c b/mrbgems/mruby-os-memsize/src/memsize.c
new file mode 100644
index 000000000..9947512cf
--- /dev/null
+++ b/mrbgems/mruby-os-memsize/src/memsize.c
@@ -0,0 +1,248 @@
+#include <mruby.h>
+#include <mruby/gc.h>
+#include <mruby/hash.h>
+#include <mruby/class.h>
+#include <mruby/object.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/array.h>
+#include <mruby/variable.h>
+#include <mruby/proc.h>
+#include <mruby/value.h>
+#include <mruby/range.h>
+
+static size_t
+os_memsize_of_irep(mrb_state* state, const struct mrb_irep *irep)
+{
+ size_t size;
+ int i;
+
+ size = (irep->slen * sizeof(mrb_sym)) +
+ (irep->plen * sizeof(mrb_code)) +
+ (irep->ilen * sizeof(mrb_code));
+
+ for(i = 0; i < irep->rlen; i++) {
+ size += os_memsize_of_irep(state, irep->reps[i]);
+ }
+ return size;
+}
+
+static size_t
+os_memsize_of_method(mrb_state* mrb, mrb_value method_obj)
+{
+ size_t size;
+ mrb_value proc_value = mrb_obj_iv_get(mrb, mrb_obj_ptr(method_obj),
+ mrb_intern_lit(mrb, "_proc"));
+ if (mrb_nil_p(proc_value)) return 0;
+ struct RProc *proc = mrb_proc_ptr(proc_value);
+
+ size = sizeof(struct RProc);
+ if (!MRB_PROC_CFUNC_P(proc)) size += os_memsize_of_irep(mrb, proc->body.irep);
+ return size;
+}
+
+static mrb_bool
+obj_is_kind_of_checked(mrb_state *mrb, mrb_value obj, const char *classname)
+{
+ mrb_value objclass = mrb_obj_value(mrb->object_class);
+
+ if (mrb_const_defined(mrb, objclass, mrb_intern_cstr(mrb, classname))) {
+ struct RClass *klass = mrb_class_get(mrb, classname);
+ return mrb_obj_is_kind_of(mrb, obj, klass);
+ }
+
+ return FALSE;
+}
+
+static size_t
+os_memsize_of_object(mrb_state* mrb, mrb_value obj)
+{
+ size_t size = 0;
+
+ switch(mrb_type(obj)) {
+ case MRB_TT_STRING:
+ size += mrb_objspace_page_slot_size();
+ if (!RSTR_EMBED_P(RSTRING(obj)) && !RSTR_SHARED_P(RSTRING(obj))) {
+ size += RSTRING_CAPA(obj);
+ size++; /* NUL terminator */
+ }
+ break;
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ case MRB_TT_SCLASS:
+ case MRB_TT_ICLASS:
+ size += mrb_gc_mark_mt_size(mrb, mrb_class_ptr(obj)) * sizeof(mrb_method_t);
+ /* fall through */
+ case MRB_TT_EXCEPTION:
+ case MRB_TT_OBJECT: {
+ size += mrb_objspace_page_slot_size();
+ size += mrb_obj_iv_tbl_memsize(obj);
+ if (obj_is_kind_of_checked(mrb, obj, "UnboundMethod") ||
+ obj_is_kind_of_checked(mrb, obj, "Method")) {
+ size += os_memsize_of_method(mrb, obj);
+ }
+ break;
+ }
+ case MRB_TT_HASH: {
+ size += mrb_objspace_page_slot_size() +
+ mrb_hash_memsize(obj);
+ break;
+ }
+ case MRB_TT_ARRAY: {
+ mrb_int len = RARRAY_LEN(obj);
+ /* Arrays that do not fit within an RArray perform a heap allocation
+ * storing an array of pointers to the original objects*/
+ size += mrb_objspace_page_slot_size();
+ if(len > MRB_ARY_EMBED_LEN_MAX) size += sizeof(mrb_value *) * len;
+ break;
+ }
+ case MRB_TT_PROC: {
+ struct RProc* proc = mrb_proc_ptr(obj);
+ size += mrb_objspace_page_slot_size();
+ size += MRB_ENV_LEN(proc->e.env) * sizeof(mrb_value);
+ if(!MRB_PROC_CFUNC_P(proc)) size += os_memsize_of_irep(mrb, proc->body.irep);
+ break;
+ }
+ case MRB_TT_RANGE:
+#ifndef MRB_RANGE_EMBED
+ size += mrb_objspace_page_slot_size() +
+ sizeof(struct mrb_range_edges);
+#endif
+ break;
+ case MRB_TT_FIBER: {
+ struct RFiber* f = (struct RFiber *)mrb_ptr(obj);
+ ptrdiff_t stack_size = f->cxt->stend - f->cxt->stbase;
+ ptrdiff_t ci_size = f->cxt->ciend - f->cxt->cibase;
+
+ size += mrb_objspace_page_slot_size() +
+ sizeof(struct mrb_context) +
+ sizeof(mrb_value) * stack_size +
+ sizeof(mrb_callinfo) * ci_size;
+ break;
+ }
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+#endif
+ case MRB_TT_INTEGER:
+ if (mrb_immediate_p(obj))
+ break;
+ case MRB_TT_RATIONAL:
+#if defined(MRB_USE_RATIONAL)
+#if defined(MRB_INT64) && defined(MRB_32BIT)
+ size += sizeof(mrb_int)*2;
+#endif
+ size += mrb_objspace_page_slot_size();
+#endif
+ break;
+
+ case MRB_TT_COMPLEX:
+#if defined(MRB_USE_COMPLEX)
+#if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32)
+ size += sizeof(mrb_float)*2;
+#endif
+ size += mrb_objspace_page_slot_size();
+#endif
+ break;
+ case MRB_TT_DATA:
+ case MRB_TT_ISTRUCT:
+ size += mrb_objspace_page_slot_size();
+ break;
+ /* zero heap size types.
+ * immediate VM stack values, contained within mrb_state, or on C stack */
+ case MRB_TT_TRUE:
+ case MRB_TT_FALSE:
+ case MRB_TT_BREAK:
+ case MRB_TT_CPTR:
+ case MRB_TT_SYMBOL:
+ case MRB_TT_FREE:
+ case MRB_TT_UNDEF:
+ case MRB_TT_ENV:
+ /* never used, silences compiler warning
+ * not having a default: clause lets the compiler tell us when there is a new
+ * TT not accounted for */
+ case MRB_TT_MAXDEFINE:
+ break;
+ }
+ return size;
+}
+
+/*
+ * call-seq:
+ * ObjectSpace.memsize_of(obj) -> Numeric
+ *
+ * Returns the amount of heap memory allocated for object in size_t units.
+ *
+ * The return value depends on the definition of size_t on that platform,
+ * therefore the value is not comparable across platform types.
+ *
+ * Immediate values such as integers, booleans, symbols and unboxed float numbers
+ * return 0. Additionally special objects which are small enough to fit inside an
+ * object pointer, termed embedded objects, will return the size of the object pointer.
+ * Strings and arrays below a compile-time defined size may be embedded.
+ */
+
+static mrb_value
+os_memsize_of(mrb_state *mrb, mrb_value self)
+{
+ size_t total;
+ mrb_value obj;
+
+ mrb_get_args(mrb, "o", &obj);
+
+ total = os_memsize_of_object(mrb, obj);
+ return mrb_fixnum_value((mrb_int)total);
+}
+
+struct os_memsize_of_all_cb_data {
+ size_t t;
+ struct RClass *type;
+};
+
+static int
+os_memsize_of_all_cb(mrb_state *mrb, struct RBasic *obj, void *d)
+{
+ struct os_memsize_of_all_cb_data *data = (struct os_memsize_of_all_cb_data *)d;
+ switch (obj->tt) {
+ case MRB_TT_FREE: case MRB_TT_ENV:
+ case MRB_TT_BREAK: case MRB_TT_ICLASS:
+ /* internal objects that should not be counted */
+ return MRB_EACH_OBJ_OK;
+ default:
+ break;
+ }
+ /* skip Proc objects for methods */
+ if (obj->c == NULL) return 0;
+ if (data->type == NULL || mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), data->type))
+ data->t += os_memsize_of_object(mrb, mrb_obj_value(obj));
+ return MRB_EACH_OBJ_OK;
+}
+
+/*
+ * call-seq:
+ * ObjectSpace.memsize_of_all([klass]) -> Numeric
+ *
+ * Return consuming memory size of all living objects of type klass.
+ *
+ */
+
+static mrb_value
+os_memsize_of_all(mrb_state *mrb, mrb_value self)
+{
+ struct os_memsize_of_all_cb_data data = { 0 };
+ mrb_get_args(mrb, "|c", &data.type);
+ mrb_objspace_each_objects(mrb, os_memsize_of_all_cb, &data);
+ return mrb_fixnum_value((mrb_int)data.t);
+}
+
+void
+mrb_mruby_os_memsize_gem_init(mrb_state *mrb)
+{
+ struct RClass *os = mrb_module_get(mrb, "ObjectSpace");
+ mrb_define_class_method(mrb, os, "memsize_of", os_memsize_of, MRB_ARGS_REQ(1));
+ mrb_define_class_method(mrb, os, "memsize_of_all", os_memsize_of_all, MRB_ARGS_OPT(1));
+}
+
+void
+mrb_mruby_os_memsize_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-os-memsize/test/memsize.rb b/mrbgems/mruby-os-memsize/test/memsize.rb
new file mode 100644
index 000000000..6be8f1b06
--- /dev/null
+++ b/mrbgems/mruby-os-memsize/test/memsize.rb
@@ -0,0 +1,63 @@
+assert 'ObjectSpace.memsize_of' do
+ # immediate literals
+ int_size = ObjectSpace.memsize_of 1
+ assert_equal int_size, 0, 'int zero'
+
+ sym_size = ObjectSpace.memsize_of :foo
+ assert_equal sym_size, 0, 'sym zero'
+
+ assert_equal ObjectSpace.memsize_of(true), int_size
+ assert_equal ObjectSpace.memsize_of(false), int_size
+
+ assert_not_equal ObjectSpace.memsize_of('a'), 0, 'memsize of str'
+
+ if __ENCODING__ == "UTF-8"
+ assert_not_equal ObjectSpace.memsize_of("こんにちは世界"), 0, 'memsize of utf8 str'
+ end
+
+ # class defs
+ class_obj_size = ObjectSpace.memsize_of Class
+ assert_not_equal class_obj_size, 0, 'Class obj not zero'
+
+ empty_class_def_size = ObjectSpace.memsize_of Class.new
+ assert_not_equal empty_class_def_size, 0, 'Class def not zero'
+
+ proc_size = ObjectSpace.memsize_of Proc.new { x = 1; x }
+ assert_not_equal proc_size, 0
+
+ class_with_methods = Class.new do
+ def foo
+ a = 0
+ a + 1
+ end
+ end
+
+ m_size = ObjectSpace.memsize_of class_with_methods.instance_method(:foo)
+ assert_not_equal m_size, 0, 'method size not zero'
+
+ # collections
+ empty_array_size = ObjectSpace.memsize_of []
+ assert_not_equal empty_array_size, 0, 'empty array size not zero'
+ assert_operator empty_array_size, :<, ObjectSpace.memsize_of(Array.new(16)), 'large array size greater than embed'
+
+ # fiber
+ empty_fiber_size = ObjectSpace.memsize_of(Fiber.new {})
+ assert_not_equal empty_fiber_size, 0, 'empty fiber not zero'
+
+ #hash
+ assert_not_equal ObjectSpace.memsize_of({}), 0, 'empty hash size not zero'
+end
+
+assert 'ObjectSpace.memsize_of_all' do
+ foo_class = Class.new do
+ def initialize
+ @a = 'a'
+ @b = 'b'
+ end
+ end
+
+ foos = Array.new(10) { foo_class.new }
+ foo_size = ObjectSpace.memsize_of(foos.first)
+
+ assert_equal ObjectSpace.memsize_of_all(foo_class), foo_size * foos.size, 'Memsize of all instance'
+end
diff --git a/mrbgems/mruby-pack/.gitignore b/mrbgems/mruby-pack/.gitignore
deleted file mode 100644
index 55ef3162f..000000000
--- a/mrbgems/mruby-pack/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-gem_*
-gem-*
-mrb-*.a
-src/*.o
-/tmp
diff --git a/mrbgems/mruby-pack/.travis.yml b/mrbgems/mruby-pack/.travis.yml
deleted file mode 100644
index ffe227284..000000000
--- a/mrbgems/mruby-pack/.travis.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-script:
- - "ruby run_test.rb all test"
diff --git a/mrbgems/mruby-pack/README.md b/mrbgems/mruby-pack/README.md
index 95733e2d5..712cf8e1c 100644
--- a/mrbgems/mruby-pack/README.md
+++ b/mrbgems/mruby-pack/README.md
@@ -1,70 +1,67 @@
-mruby-pack (pack / unpack)
-=========
+# mruby-pack (pack / unpack)
mruby-pack provides `Array#pack` and `String#unpack` for mruby.
-
## Installation
-Add the line below into your `build_config.rb`:
+
+Add the line below into your build configuration:
```
- conf.gem :github => 'iij/mruby-pack'
+ conf.gem :core => '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 *
-
+- 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.
+Copyright (c) 2017 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
+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
+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
+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.
-
diff --git a/mrbgems/mruby-pack/mrbgem.rake b/mrbgems/mruby-pack/mrbgem.rake
index 6e8375d1e..f1c0306b1 100644
--- a/mrbgems/mruby-pack/mrbgem.rake
+++ b/mrbgems/mruby-pack/mrbgem.rake
@@ -1,6 +1,6 @@
MRuby::Gem::Specification.new('mruby-pack') do |spec|
spec.license = 'MIT'
- spec.authors = 'Internet Initiative Japan Inc.'
+ spec.authors = ['Internet Initiative Japan Inc.', 'mruby developers']
spec.summary = 'Array#pack and String#unpack method'
spec.cc.include_paths << "#{build.root}/src"
diff --git a/mrbgems/mruby-pack/packtest.rb b/mrbgems/mruby-pack/packtest.rb
deleted file mode 100644
index 459447af7..000000000
--- a/mrbgems/mruby-pack/packtest.rb
+++ /dev/null
@@ -1,157 +0,0 @@
-# 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"
diff --git a/mrbgems/mruby-pack/run_test.rb b/mrbgems/mruby-pack/run_test.rb
deleted file mode 100644
index d9566a2a6..000000000
--- a/mrbgems/mruby-pack/run_test.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/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
diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c
index f970d9339..df379c003 100644
--- a/mrbgems/mruby-pack/src/pack.c
+++ b/mrbgems/mruby-pack/src/pack.c
@@ -2,32 +2,35 @@
** pack.c - Array#pack, String#unpack
*/
-#include "mruby.h"
-#include "error.h"
+#include <mruby.h>
+#include "mruby/error.h"
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/numeric.h"
#include "mruby/string.h"
#include "mruby/variable.h"
+#include "mruby/endian.h"
#include <ctype.h>
#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
#include <string.h>
+#define INT_OVERFLOW_P(n) ((n) < MRB_INT_MIN || (n) > MRB_INT_MAX)
+#define UINT_OVERFLOW_P(n) ((n) > MRB_INT_MAX)
+
struct tmpl {
mrb_value str;
int idx;
};
-enum {
+enum pack_dir {
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_BER, /* w */
PACK_DIR_UTF8, /* U */
//PACK_DIR_BER,
PACK_DIR_DOUBLE, /* E */
@@ -35,44 +38,37 @@ enum {
PACK_DIR_STR, /* A */
PACK_DIR_HEX, /* h */
PACK_DIR_BASE64, /* m */
+ PACK_DIR_QENC, /* M */
PACK_DIR_NUL, /* x */
+ PACK_DIR_BACK, /* X */
+ PACK_DIR_ABS, /* @ */
PACK_DIR_INVALID
};
-enum {
+enum pack_type {
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
+#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 */
-static int littleendian = 0;
+#define PACK_BASE64_IGNORE 0xff
+#define PACK_BASE64_PADDING 0xfe
const static unsigned char base64chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static signed char base64_dec_tab[128];
-
-
-static int
-check_little_endian(void)
-{
- unsigned int n = 1;
- return (*(unsigned char *)&n == 1);
-}
+static unsigned char base64_dec_tab[128];
static unsigned int
hex2int(unsigned char ch)
@@ -84,7 +80,7 @@ hex2int(unsigned char ch)
else if (ch >= 'a' && ch <= 'f')
return 10 + (ch - 'a');
else
- return 0;
+ return -1;
}
static void
@@ -98,9 +94,9 @@ make_base64_dec_tab(void)
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;
+ base64_dec_tab['+'+0] = 62;
+ base64_dec_tab['/'+0] = 63;
+ base64_dec_tab['='+0] = PACK_BASE64_PADDING;
}
static mrb_value
@@ -124,7 +120,7 @@ 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);
+ RSTRING_PTR(str)[sidx] = (char)mrb_integer(o);
return 1;
}
@@ -144,7 +140,7 @@ pack_s(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int fl
uint16_t n;
str = str_len_ensure(mrb, str, sidx + 2);
- n = (uint16_t)mrb_fixnum(o);
+ n = (uint16_t)mrb_integer(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
RSTRING_PTR(str)[sidx+0] = n % 256;
RSTRING_PTR(str)[sidx+1] = n / 256;
@@ -178,7 +174,7 @@ pack_l(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int fl
uint32_t n;
str = str_len_ensure(mrb, str, sidx + 4);
- n = (uint32_t)mrb_fixnum(o);
+ n = (uint32_t)mrb_integer(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff);
RSTRING_PTR(str)[sidx+1] = (char)(n >> 8);
@@ -193,6 +189,39 @@ pack_l(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int fl
return 4;
}
+#ifndef MRB_INT64
+static void
+u32tostr(char *buf, size_t len, uint32_t n)
+{
+#ifdef MRB_NO_STDIO
+ char *bufend = buf + len;
+ char *p = bufend - 1;
+
+ if (len < 1) {
+ return;
+ }
+
+ *p -- = '\0';
+ len --;
+
+ if (n > 0) {
+ for (; len > 0 && n > 0; len --, n /= 10) {
+ *p -- = '0' + (n % 10);
+ }
+ p ++;
+ }
+ else if (len > 0) {
+ *p = '0';
+ len --;
+ }
+
+ memmove(buf, p, bufend - p);
+#else
+ snprintf(buf, len, "%" PRIu32, n);
+#endif /* MRB_NO_STDIO */
+}
+#endif /* MRB_INT64 */
+
static int
unpack_l(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
{
@@ -214,24 +243,17 @@ unpack_l(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, un
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;
+ n = (int32_t)ul;
} 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);
+ if (UINT_OVERFLOW_P(ul)) {
+ u32tostr(msg, sizeof(msg), ul);
+ mrb_raisef(mrb, E_RANGE_ERROR, "cannot unpack to Integer: %s", msg);
}
#endif
n = ul;
}
- mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, n));
return 4;
}
@@ -241,7 +263,7 @@ pack_q(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int fl
uint64_t n;
str = str_len_ensure(mrb, str, sidx + 8);
- n = (uint64_t)mrb_fixnum(o);
+ n = (uint64_t)mrb_integer(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff);
RSTRING_PTR(str)[sidx+1] = (char)(n >> 8);
@@ -264,6 +286,59 @@ pack_q(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int fl
return 8;
}
+static void
+u64tostr(char *buf, size_t len, uint64_t n)
+{
+#ifdef MRB_NO_STDIO
+ char *bufend = buf + len;
+ char *p = bufend - 1;
+
+ if (len < 1) {
+ return;
+ }
+
+ *p -- = '\0';
+ len --;
+
+ if (n > 0) {
+ for (; len > 0 && n > 0; len --, n /= 10) {
+ *p -- = '0' + (n % 10);
+ }
+ p ++;
+ }
+ else if (len > 0) {
+ *p = '0';
+ len --;
+ }
+
+ memmove(buf, p, bufend - p);
+#else
+ snprintf(buf, len, "%" PRIu64, n);
+#endif /* MRB_NO_STDIO */
+}
+
+#ifndef MRB_INT64
+static void
+i64tostr(char *buf, size_t len, int64_t n)
+{
+#ifdef MRB_NO_STDIO
+ if (len < 1) {
+ return;
+ }
+
+ if (n < 0) {
+ *buf ++ = '-';
+ len --;
+ n = -n;
+ }
+
+ u64tostr(buf, len, (uint64_t)n);
+#else
+ snprintf(buf, len, "%" PRId64, n);
+#endif /* MRB_NO_STDIO */
+}
+#endif /* MRB_INT64 */
+
static int
unpack_q(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
{
@@ -286,23 +361,65 @@ unpack_q(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, un
}
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);
+#ifndef MRB_INT64
+ if (INT_OVERFLOW_P(sll)) {
+ i64tostr(msg, sizeof(msg), sll);
+ mrb_raisef(mrb, E_RANGE_ERROR, "cannot unpack to Integer: %s", msg);
}
- n = sll;
+#endif
+ n = (mrb_int)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);
+ if (UINT_OVERFLOW_P(ull)) {
+ u64tostr(msg, sizeof(msg), ull);
+ mrb_raisef(mrb, E_RANGE_ERROR, "cannot unpack to Integer: %s", msg);
}
- n = ull;
+ n = (mrb_int)ull;
}
- mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, n));
return 8;
}
-#ifndef MRB_WITHOUT_FLOAT
+static int
+pack_w(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
+{
+ mrb_int n = mrb_integer(o);
+ size_t i;
+ char *p;
+
+ if (n < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "can't compress negative numbers");
+ }
+ for (i=1; i<sizeof(mrb_int)+1; i++) {
+ mrb_int mask = ~((1L<<(7*i))-1);
+ if ((n & mask) == 0) break;
+ }
+ str = str_len_ensure(mrb, str, sidx + i);
+ p = RSTRING_PTR(str);
+ for (mrb_int j=i; j>0; p++,j--) {
+ mrb_int x = (n>>(7*(j-1)))&0x7f;
+ *p = (char)x;
+ if (j > 1) *p |= 0x80;
+ }
+ return i;
+}
+
+static int
+unpack_w(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
+{
+ mrb_int i, n = 0;
+ const unsigned char *p = src;
+ const unsigned char *e = p + srclen;
+
+ for (i=1; p<e; p++,i++) {
+ n <<= 7;
+ n |= *p & 0x7f;
+ if ((*p & 0x80) == 0) break;
+ }
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, n));
+ return i;
+}
+
+#ifndef MRB_NO_FLOAT
static int
pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
{
@@ -313,21 +430,23 @@ pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned i
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];
+ if (littleendian) {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
+ }
+ else {
+ 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];
+ if (littleendian) {
+ for (i = 0; i < 8; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ }
+ }
+ else {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
}
-#endif
}
return 8;
@@ -341,21 +460,23 @@ unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value a
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];
+ if (littleendian) {
+ memcpy(buffer, src, 8);
+ }
+ else {
+ 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];
+ if (littleendian) {
+ for (i = 0; i < 8; ++i) {
+ buffer[8 - i - 1] = src[i];
+ }
+ }
+ else {
+ memcpy(buffer, src, 8);
}
-#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, d));
@@ -372,21 +493,23 @@ pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned in
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];
+ if (littleendian) {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
+ }
+ else {
+ 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];
+ if (littleendian) {
+ for (i = 0; i < 4; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ }
+ }
+ else {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
}
-#endif
}
return 4;
@@ -400,21 +523,23 @@ unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ar
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];
+ if (littleendian) {
+ memcpy(buffer, src, 4);
+ }
+ else {
+ 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];
+ if (littleendian) {
+ for (i = 0; i < 4; ++i) {
+ buffer[4 - i - 1] = src[i];
+ }
+ }
+ else {
+ memcpy(buffer, src, 4);
}
-#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, f));
@@ -423,18 +548,13 @@ unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ar
#endif
static int
-pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count, unsigned int flags)
+pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, int 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);
+ c = (uint32_t)mrb_integer(o);
/* Unicode character */
/* from mruby-compiler gem */
@@ -461,9 +581,6 @@ pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count,
len = 4;
}
else {
-#ifndef MRB_WITHOUT_FLOAT
-range_error:
-#endif
mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range");
}
@@ -509,8 +626,8 @@ utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %d bytes, given %d bytes)",
+ n, *lenp);
}
*lenp = n--;
if (n != 0) {
@@ -548,7 +665,7 @@ unpack_utf8(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary
}
static int
-pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
+pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count, unsigned int flags)
{
mrb_int copylen, slen, padlen;
char *dptr, *dptr0, pad, *sptr;
@@ -586,7 +703,7 @@ pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, u
}
static int
-unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, unsigned int flags)
+unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
{
mrb_value dst;
const char *cp, *sptr;
@@ -607,7 +724,7 @@ unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, u
}
}
else if (!(flags & PACK_FLAG_a)) { /* "A" */
- while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
+ while (copylen > 0 && (sptr[copylen - 1] == '\0' || ISSPACE(sptr[copylen - 1]))) {
copylen--;
}
}
@@ -620,7 +737,7 @@ unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, u
static int
-pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
+pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count, unsigned int flags)
{
unsigned int a, ashift, b, bshift;
long slen;
@@ -651,10 +768,12 @@ pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, u
a = b = 0;
if (slen > 0) {
a = hex2int(*sptr++);
+ if (a < 0) break;
slen--;
}
if (slen > 0) {
b = hex2int(*sptr++);
+ if (b < 0) break;
slen--;
}
*dptr++ = (a << ashift) + (b << bshift);
@@ -705,14 +824,13 @@ unpack_h(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, un
}
}
- dst = mrb_str_resize(mrb, dst, dptr - dptr0);
+ dst = mrb_str_resize(mrb, dst, (mrb_int)(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)
+pack_m(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count)
{
mrb_int dstlen;
unsigned long l;
@@ -778,7 +896,7 @@ pack_m(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, u
}
static int
-unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int flags)
+unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary)
{
mrb_value dst;
int dlen;
@@ -801,13 +919,13 @@ unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int
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++;
- }
+ 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);
}
@@ -828,31 +946,123 @@ unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int
}
done:
- dst = mrb_str_resize(mrb, dst, dptr - dptr0);
+ dst = mrb_str_resize(mrb, dst, (mrb_int)(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)
+pack_M(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, int count)
+{
+ static const char hex_table[] = "0123456789ABCDEF";
+ char buff[1024];
+ char *s = RSTRING_PTR(src);
+ char *send = s + RSTRING_LEN(src);
+ int i = 0, n = 0, prev = EOF;
+ int dlen = 0;
+
+ if (count <= 1) count = 72;
+ while (s < send) {
+ if ((*s > 126) ||
+ (*s < 32 && *s != '\n' && *s != '\t') ||
+ (*s == '=')) {
+ buff[i++] = '=';
+ buff[i++] = hex_table[(*s & 0xf0) >> 4];
+ buff[i++] = hex_table[*s & 0x0f];
+ n += 3;
+ prev = EOF;
+ }
+ else if (*s == '\n') {
+ if (prev == ' ' || prev == '\t') {
+ buff[i++] = '=';
+ buff[i++] = *s;
+ }
+ buff[i++] = *s;
+ n = 0;
+ prev = *s;
+ }
+ else {
+ buff[i++] = *s;
+ n++;
+ prev = *s;
+ }
+ if (n > count) {
+ buff[i++] = '=';
+ buff[i++] = '\n';
+ n = 0;
+ prev = '\n';
+ }
+ if (i > 1024 - 5) {
+ str_len_ensure(mrb, dst, didx+i);
+ memcpy(RSTRING_PTR(dst), buff, i);
+ dlen += i;
+ i = 0;
+ }
+ s++;
+ }
+ if (n > 0) {
+ buff[i++] = '=';
+ buff[i++] = '\n';
+ }
+ if (i > 0) {
+ str_len_ensure(mrb, dst, didx+i);
+ memcpy(RSTRING_PTR(dst), buff, i);
+ dlen += i;
+ }
+ return dlen;
+}
+
+static int
+unpack_M(mrb_state *mrb, const void *src, int slen, mrb_value ary)
+{
+ mrb_value buf = mrb_str_new(mrb, 0, slen);
+ const char *s = (const char*)src, *ss = s;
+ const char *send = s + slen;
+ char *ptr = RSTRING_PTR(buf);
+ int c1, c2;
+
+ while (s < send) {
+ if (*s == '=') {
+ if (++s == send) break;
+ if (s+1 < send && *s == '\r' && *(s+1) == '\n')
+ s++;
+ if (*s != '\n') {
+ if ((c1 = hex2int(*s)) == -1) break;
+ if (++s == send) break;
+ if ((c2 = hex2int(*s)) == -1) break;
+ *ptr++ = (char)(c1 << 4 | c2);
+ }
+ }
+ else {
+ *ptr++ = *s;
+ }
+ s++;
+ ss = s;
+ }
+ buf = mrb_str_resize(mrb, buf, (mrb_int)(ptr - RSTRING_PTR(buf)));
+ mrb_str_cat(mrb, buf, ss, send-ss);
+ mrb_ary_push(mrb, ary, buf);
+ return slen;
+}
+
+static int
+pack_x(mrb_state *mrb, mrb_value dst, mrb_int didx, int count)
{
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)
+
+static void
+check_x(mrb_state *mrb, int a, int count, char c)
{
- if (count < 0) return slen;
- if (slen < count) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "x outside of string");
+ if (a < count) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c outside of string", c);
}
- return count;
}
static void
@@ -869,10 +1079,12 @@ has_tmpl(const struct tmpl *tmpl)
}
static void
-read_tmpl(mrb_state *mrb, struct tmpl *tmpl, int *dirp, int *typep, int *sizep, int *countp, unsigned int *flagsp)
+read_tmpl(mrb_state *mrb, struct tmpl *tmpl, enum pack_dir *dirp, enum pack_type *typep, int *sizep, int *countp, unsigned int *flagsp)
{
mrb_int t, tlen;
- int ch, dir, type, size = 0;
+ int ch, size = 0;
+ enum pack_dir dir;
+ enum pack_type type;
int count = 1;
unsigned int flags = 0;
const char *tptr;
@@ -956,7 +1168,7 @@ 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)));
+ mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %d", (int)sizeof(int));
}
break;
case 'i':
@@ -965,7 +1177,7 @@ 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)));
+ mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %d", (int)sizeof(int));
}
break;
case 'L':
@@ -979,10 +1191,20 @@ alias:
size = 4;
flags |= PACK_FLAG_SIGNED;
break;
+ case 'w':
+ dir = PACK_DIR_BER;
+ type = PACK_TYPE_INTEGER;
+ flags |= PACK_FLAG_SIGNED;
+ break;
case 'm':
dir = PACK_DIR_BASE64;
type = PACK_TYPE_STRING;
- flags |= PACK_FLAG_WIDTH;
+ flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
+ break;
+ case 'M':
+ dir = PACK_DIR_QENC;
+ type = PACK_TYPE_STRING;
+ flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
break;
case 'N': /* = "L>" */
dir = PACK_DIR_LONG;
@@ -1038,11 +1260,23 @@ alias:
dir = PACK_DIR_NUL;
type = PACK_TYPE_NONE;
break;
+ case 'X':
+ dir = PACK_DIR_BACK;
+ type = PACK_TYPE_NONE;
+ break;
+ case '@':
+ dir = PACK_DIR_ABS;
+ 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;
+ case 'p': case 'P':
+ case '%':
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c is not supported", (char)t);
+ break;
default:
dir = PACK_DIR_INVALID;
type = PACK_TYPE_NONE;
@@ -1051,22 +1285,24 @@ alias:
/* 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");
- }
+ ch = tptr[tmpl->idx];
+ if (ISDIGIT(ch)) {
+ char *e;
+ mrb_int n = mrb_int_read(tptr+tmpl->idx, tptr+tlen, &e);
+ if (e == NULL || n > INT_MAX) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length");
}
- continue; /* special case */
+ count = (int)n;
+ tmpl->idx = e - tptr;
+ continue;
} else if (ch == '*') {
- count = -1;
+ if (type == PACK_TYPE_NONE)
+ count = 0;
+ else
+ 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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%c' allowed only after types sSiIlLqQ", ch);
}
if (ch == '_' || ch == '!') {
flags |= PACK_FLAG_s;
@@ -1075,10 +1311,11 @@ alias:
} else if (ch == '>') {
flags |= PACK_FLAG_GT;
}
- } else {
- tmpl->idx--;
+ }
+ else {
break;
}
+ tmpl->idx++;
}
if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) {
@@ -1100,7 +1337,9 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
struct tmpl tmpl;
int count;
unsigned int flags;
- int dir, ridx, size, type;
+ enum pack_dir dir;
+ enum pack_type type;
+ int ridx, size;
prepare_tmpl(mrb, &tmpl);
@@ -1113,10 +1352,28 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
if (dir == PACK_DIR_INVALID)
continue;
else if (dir == PACK_DIR_NUL) {
- ridx += pack_x(mrb, mrb_nil_value(), result, ridx, count, flags);
- continue;
+ grow:
+ if (ridx > INT_MAX - count) goto overflow;
+ ridx += pack_x(mrb, result, ridx, count);
+ continue;
+ }
+ else if (dir == PACK_DIR_BACK) {
+ check_x(mrb, ridx, count, 'X');
+ ridx -= count;
+ continue;
+ }
+ else if (dir == PACK_DIR_ABS) {
+ count -= ridx;
+ if (count > 0) goto grow;
+ count = -count;
+ check_x(mrb, ridx, count, '@');
+ ridx -= count;
+ continue;
}
+ if ((flags & PACK_FLAG_WIDTH) && aidx >= RARRAY_LEN(ary)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments");
+ }
for (; aidx < RARRAY_LEN(ary); aidx++) {
if (count == 0 && !(flags & PACK_FLAG_WIDTH))
break;
@@ -1124,16 +1381,18 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
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) {
+ }
+#ifndef MRB_NO_FLOAT
+ else if (type == PACK_TYPE_FLOAT) {
if (!mrb_float_p(o)) {
- mrb_float f = mrb_to_flo(mrb, o);
+ mrb_float f = mrb_as_float(mrb, o);
o = mrb_float_value(mrb, f);
}
+ }
#endif
- } else if (type == PACK_TYPE_STRING) {
+ 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)));
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %T into String", o);
}
}
@@ -1150,8 +1409,14 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
case PACK_DIR_QUAD:
ridx += pack_q(mrb, o, result, ridx, flags);
break;
+ case PACK_DIR_BER:
+ ridx += pack_w(mrb, o, result, ridx, flags);
+ break;
case PACK_DIR_BASE64:
- ridx += pack_m(mrb, o, result, ridx, count, flags);
+ ridx += pack_m(mrb, o, result, ridx, count);
+ break;
+ case PACK_DIR_QENC:
+ ridx += pack_M(mrb, o, result, ridx, count);
break;
case PACK_DIR_HEX:
ridx += pack_h(mrb, o, result, ridx, count, flags);
@@ -1159,7 +1424,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
case PACK_DIR_STR:
ridx += pack_a(mrb, o, result, ridx, count, flags);
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case PACK_DIR_DOUBLE:
ridx += pack_double(mrb, o, result, ridx, flags);
break;
@@ -1173,7 +1438,8 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
default:
break;
}
- if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
+ if (dir == PACK_DIR_STR || dir == PACK_DIR_BASE64 || dir == PACK_DIR_HEX) {
+ /* always consumes 1 entry */
aidx++;
break;
}
@@ -1182,6 +1448,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
}
}
if (ridx < 0) {
+ overflow:
mrb_raise(mrb, E_RANGE_ERROR, "negative (or overflowed) template size");
}
}
@@ -1197,7 +1464,9 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
struct tmpl tmpl;
int count;
unsigned int flags;
- int dir, size, type;
+ enum pack_dir dir;
+ enum pack_type type;
+ int size;
int srcidx, srclen;
const unsigned char *sptr;
@@ -1213,7 +1482,18 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
if (dir == PACK_DIR_INVALID)
continue;
else if (dir == PACK_DIR_NUL) {
- srcidx += unpack_x(mrb, sptr, srclen - srcidx, result, count, flags);
+ check_x(mrb, srclen-srcidx, count, 'x');
+ srcidx += count;
+ continue;
+ }
+ else if (dir == PACK_DIR_BACK) {
+ check_x(mrb, srcidx, count, 'X');
+ srcidx -= count;
+ continue;
+ }
+ else if (dir == PACK_DIR_ABS) {
+ check_x(mrb, srclen, count, '@');
+ srcidx = count;
continue;
}
@@ -1226,6 +1506,14 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
case PACK_DIR_STR:
srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
break;
+ case PACK_DIR_BASE64:
+ srcidx += unpack_m(mrb, sptr, srclen - srcidx, result);
+ break;
+ case PACK_DIR_QENC:
+ srcidx += unpack_M(mrb, sptr, srclen - srcidx, result);
+ break;
+ default:
+ break;
}
continue;
}
@@ -1252,10 +1540,10 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
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);
+ case PACK_DIR_BER:
+ srcidx += unpack_w(mrb, sptr, srclen - srcidx, result, flags);
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case PACK_DIR_FLOAT:
srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
break;
@@ -1276,7 +1564,12 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
if (single) break;
}
- if (single) return RARRAY_PTR(result)[0];
+ if (single) {
+ if (RARRAY_LEN(result) > 0) {
+ return RARRAY_PTR(result)[0];
+ }
+ return mrb_nil_value();
+ }
return result;
}
@@ -1295,7 +1588,6 @@ mrb_pack_unpack1(mrb_state *mrb, mrb_value str)
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));
diff --git a/mrbgems/mruby-pack/test/pack.rb b/mrbgems/mruby-pack/test/pack.rb
index 1788c2b72..16db6607b 100644
--- a/mrbgems/mruby-pack/test/pack.rb
+++ b/mrbgems/mruby-pack/test/pack.rb
@@ -1,102 +1,79 @@
-# pack & unpack 'm' (base64)
-assert('[""].pack("m")') do
- ary = ""
- str = ""
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
-end
+# coding: utf-8
+PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
-assert('["\0"].pack("m")') do
- ary = "\0"
- str = "AA==\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+def assert_pack tmpl, packed, unpacked
+ t = tmpl.inspect
+ assert "assert_pack" do
+ assert_equal packed, unpacked.pack(tmpl), "#{unpacked.inspect}.pack(#{t})"
+ assert_equal unpacked, packed.unpack(tmpl), "#{packed.inspect}.unpack(#{t})"
+ end
end
-assert('["\0\0"].pack("m")') do
- ary = "\0\0"
- str = "AAA=\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
-end
+# pack & unpack 'm' (base64)
+assert('pack("m")') do
+ assert_pack "m", "", [""]
+ assert_pack "m", "AA==\n", ["\0"]
+ assert_pack "m", "AAA=\n", ["\0\0"]
+ assert_pack "m", "AAAA\n", ["\0\0\0"]
+ assert_pack "m", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
-assert('["\0\0\0"].pack("m")') do
- ary = "\0\0\0"
- str = "AAAA\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
-end
+ ary = ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
+ assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m")
+ assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m")
-assert('["abc..xyzABC..XYZ"].pack("m")') do
- ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m") == "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n"
+ assert_equal "QQ==\n", ["A", "B"].pack("m50")
+ assert_equal ["A"], "QQ==\n".unpack("m50")
+ assert_equal "QQ==Qg==", ["A", "B"].pack("m0 m0")
+ assert_equal ["A", "B"], "QQ==Qg==".unpack("m10 m10")
+ assert_pack "m0", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
end
-assert('"YWJ...".unpack("m") should "abc..xyzABC..XYZ"') do
- str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m") == [str] and
- "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m") == [str]
+# pack & unpack 'M' (Quoted-printable)
+assert('pack("M")') do
+ assert_pack "M", "123=\n", ["123"]
+ assert_pack "M", "=3D\n", ["=\n"]
+ assert_pack "M", "=E3=81=82=\n", ["あ"]
+
+ assert_equal ["123"], "123=\n".unpack("M")
+ assert_equal ["=\n"], "=3D\n".unpack("M")
+ assert_equal ["あ"], "=E3=81=82=\n".unpack("M")
end
# pack & unpack 'H'
-assert('["3031"].pack("H*")') do
- ary = "3031"
- str = "01"
- [ary].pack("H*") == str and
- str.unpack("H*") == [ary]
+assert('pack("H")') do
+ assert_pack "H*", "01", ["3031"]
+ assert_pack "H*", "\020", ["10"]
end
-assert('["10"].pack("H*")') do
- ary = "10"
- str = "\020"
- [ary].pack("H*") == str and
- str.unpack("H*") == [ary]
+assert('pack("C")') do
+ assert_pack "C*", "\x00\x01\x7F\x80\xFF", [0, 1, 127, 128, 255]
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
+assert('pack("a")') do
+ assert_equal "a", ["abc"].pack("a")
+ assert_equal "abc", ["abc"].pack("a*")
+ assert_equal "abc\0", ["abc"].pack("a4")
-# pack "a"
-assert('["abc"].pack("a")') do
- ["abc"].pack("a") == "a" and
- ["abc"].pack("a*") == "abc" and
- ["abc"].pack("a4") == "abc\0"
+ assert_equal ["abc\0"], "abc\0".unpack("a4")
+ assert_equal ["abc "], "abc ".unpack("a4")
end
-# upack "a"
-assert('["abc"].pack("a")') do
- "abc\0".unpack("a4") == ["abc\0"] and
- "abc ".unpack("a4") == ["abc "]
-end
+assert('pack("A")') do
+ assert_equal "a", ["abc"].pack("A")
+ assert_equal "abc", ["abc"].pack("A*")
+ assert_equal "abc ", ["abc"].pack("A4")
-# 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"]
+ assert_equal ["abc"], "abc\0".unpack("A4")
+ assert_equal ["abc"], "abc ".unpack("A4")
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)
+ assert_equal "\000\001\000\002", [1, 2].pack("nn")
end
-PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
-
assert 'pack float' do
+ skip unless Object.const_defined?(:Float)
assert_pack 'e', "\x00\x00@@", [3.0]
assert_pack 'g', "@@\x00\x00", [3.0]
@@ -110,6 +87,7 @@ assert 'pack float' do
end
assert 'pack double' do
+ skip unless Object.const_defined?(:Float)
assert_pack 'E', "\x00\x00\x00\x00\x00\x00\b@", [3.0]
assert_pack 'G', "@\b\x00\x00\x00\x00\x00\x00", [3.0]
@@ -146,6 +124,12 @@ assert 'pack/unpack "I"' do
assert_pack 'I', str, [12345]
end
+assert 'pack/unpack "w"' do
+ for x in [0,1,127,128,16383,16384,65535,65536]
+ assert_equal [x], [x].pack("w").unpack("w")
+ end
+end
+
assert 'pack/unpack "U"' do
assert_equal [], "".unpack("U")
assert_equal [], "".unpack("U*")
diff --git a/mrbgems/mruby-print/mrblib/print.rb b/mrbgems/mruby-print/mrblib/print.rb
index fa83c47de..96ee71fe9 100644
--- a/mrbgems/mruby-print/mrblib/print.rb
+++ b/mrbgems/mruby-print/mrblib/print.rb
@@ -4,61 +4,54 @@
# ISO 15.3.1
module Kernel
##
- # Invoke method +print+ on STDOUT and passing +*args+
+ # Print human readable object description
#
- # ISO 15.3.1.2.10
- def print(*args)
+ # ISO 15.3.1.2.9
+ # ISO 15.3.1.3.34
+ def p(*args)
i = 0
len = args.size
while i < len
- __printstr__ args[i].to_s
+ __printstr__ args[i].inspect
+ __printstr__ "\n"
i += 1
end
+ args.__svalue
end
- ##
- # Invoke method +puts+ on STDOUT and passing +*args*+
- #
- # ISO 15.3.1.2.11
- def puts(*args)
+ # 15.3.1.2.10
+ # 15.3.1.3.35
+ def print(*args)
i = 0
len = args.size
while i < len
- s = args[i].to_s
- __printstr__ s
- __printstr__ "\n" if (s[-1] != "\n")
+ __printstr__ args[i].to_s
i += 1
end
- __printstr__ "\n" if len == 0
- nil
end
- ##
- # Print human readable object description
- #
- # ISO 15.3.1.3.34
- def p(*args)
+ # 15.3.1.2.11
+ # 15.3.1.3.39
+ def puts(*args)
i = 0
len = args.size
while i < len
- __printstr__ args[i].inspect
- __printstr__ "\n"
+ s = args[i]
+ if s.kind_of?(Array)
+ puts(*s)
+ else
+ s = s.to_s
+ __printstr__ s
+ __printstr__ "\n" if (s[-1] != "\n")
+ end
i += 1
end
- args.__svalue
+ __printstr__ "\n" if len == 0
+ nil
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
+ def printf(*args)
+ __printstr__(sprintf(*args))
+ nil
end
end
diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c
index e181b06e0..c98bfb7bf 100644
--- a/mrbgems/mruby-print/src/print.c
+++ b/mrbgems/mruby-print/src/print.c
@@ -1,6 +1,10 @@
#include <mruby.h>
+
+#ifdef MRB_NO_STDIO
+# error print conflicts 'MRB_NO_STDIO' in your build configuration
+#endif
+
#include <mruby/string.h>
-#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if defined(_WIN32)
@@ -13,41 +17,34 @@
#endif
static void
-printstr(mrb_state *mrb, mrb_value obj)
+printstr(mrb_state *mrb, const char *p, mrb_int len)
{
- 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
+ if (isatty(fileno(stdout))) {
+ DWORD written;
+ int wlen = MultiByteToWideChar(CP_UTF8, 0, p, (int)len, NULL, 0);
+ wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
+ if (MultiByteToWideChar(CP_UTF8, 0, p, (int)len, utf16, wlen) > 0) {
+ utf16[wlen] = 0;
+ WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
+ utf16, (DWORD)wlen, &written, NULL);
+ }
+ mrb_free(mrb, utf16);
+ } else
#endif
- fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
- fflush(stdout);
- }
+ fwrite(p, (size_t)len, 1, stdout);
+ fflush(stdout);
}
-/* 15.3.1.2.9 */
-/* 15.3.1.3.34 */
-mrb_value
+static mrb_value
mrb_printstr(mrb_state *mrb, mrb_value self)
{
- mrb_value argv;
-
- mrb_get_args(mrb, "o", &argv);
- printstr(mrb, argv);
+ mrb_value s = mrb_get_arg1(mrb);
- return argv;
+ if (mrb_string_p(s)) {
+ printstr(mrb, RSTRING_PTR(s), RSTRING_LEN(s));
+ }
+ return s;
}
void
diff --git a/mrbgems/mruby-proc-binding/mrbgem.rake b/mrbgems/mruby-proc-binding/mrbgem.rake
new file mode 100644
index 000000000..425aac847
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/mrbgem.rake
@@ -0,0 +1,9 @@
+MRuby::Gem::Specification.new('mruby-proc-binding') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Proc#binding method'
+
+ spec.add_dependency('mruby-binding-core', :core => 'mruby-binding-core')
+ spec.add_test_dependency('mruby-binding', :core => 'mruby-binding')
+ spec.add_test_dependency('mruby-compiler', :core => 'mruby-compiler')
+end
diff --git a/mrbgems/mruby-proc-binding/src/proc-binding.c b/mrbgems/mruby-proc-binding/src/proc-binding.c
new file mode 100644
index 000000000..82d9d1d51
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/src/proc-binding.c
@@ -0,0 +1,52 @@
+#include <mruby.h>
+#include <mruby/presym.h>
+#include <mruby/proc.h>
+#include <mruby/variable.h>
+
+void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack);
+
+/* provided by mruby-proc-ext */
+mrb_value mrb_proc_source_location(mrb_state *mrb, struct RProc *p);
+
+/* provided by mruby-binding-core */
+mrb_value mrb_binding_alloc(mrb_state *mrb);
+struct RProc *mrb_binding_wrap_lvspace(mrb_state *mrb, const struct RProc *proc, struct REnv **envp);
+
+static mrb_value
+mrb_proc_binding(mrb_state *mrb, mrb_value procval)
+{
+ mrb_value binding = mrb_binding_alloc(mrb);
+ const struct RProc *proc = mrb_proc_ptr(procval);
+ struct REnv *env;
+
+ mrb_value receiver;
+ if (!proc || MRB_PROC_CFUNC_P(proc) || !proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
+ env = NULL;
+ proc = NULL;
+ receiver = mrb_nil_value();
+ }
+ else {
+ env = MRB_PROC_ENV(proc);
+ mrb_assert(env);
+ proc = proc->upper;
+ receiver = MRB_ENV_LEN(env) > 0 ? env->stack[0] : mrb_nil_value();
+ }
+
+ proc = mrb_binding_wrap_lvspace(mrb, proc, &env);
+ mrb_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value((void *)proc));
+ mrb_iv_set(mrb, binding, MRB_SYM(recv), receiver);
+ mrb_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env));
+ mrb_iv_set(mrb, binding, MRB_SYM(source_location), mrb_proc_source_location(mrb, mrb_proc_ptr(procval)));
+ return binding;
+}
+
+void
+mrb_mruby_proc_binding_gem_init(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->proc_class, "binding", mrb_proc_binding, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_proc_binding_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-proc-binding/test/proc-binding.c b/mrbgems/mruby-proc-binding/test/proc-binding.c
new file mode 100644
index 000000000..ec071b920
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/test/proc-binding.c
@@ -0,0 +1,14 @@
+#include <mruby.h>
+#include <mruby/compile.h>
+
+static mrb_value
+proc_in_c(mrb_state *mrb, mrb_value self)
+{
+ return mrb_load_string(mrb, "proc { |a, b| a + b }");
+}
+
+void
+mrb_mruby_proc_binding_gem_test(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->object_class, "proc_in_c", proc_in_c, MRB_ARGS_NONE());
+}
diff --git a/mrbgems/mruby-proc-binding/test/proc-binding.rb b/mrbgems/mruby-proc-binding/test/proc-binding.rb
new file mode 100644
index 000000000..b28d8b1dd
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/test/proc-binding.rb
@@ -0,0 +1,22 @@
+assert "Proc#binding" do
+ block = ->(i) {}
+ a, b, c = 1, 2, 3
+ bind = block.binding
+ assert_equal([:a, :b, :bind, :block, :c], bind.local_variables.sort)
+ assert_equal(1, bind.local_variable_get(:a))
+ assert_equal(5, bind.eval("b + c"))
+ bind.local_variable_set(:x, 9)
+ assert_equal(9, bind.local_variable_get(:x))
+end
+
+assert("Binding#source_location after Proc#binding") do
+ skip unless -> {}.source_location
+
+ block, source_location = -> {}, [__FILE__, __LINE__]
+ assert_equal source_location, block.binding.source_location
+end
+
+assert "Proc#binding and .eval from C" do
+ bind = proc_in_c.binding
+ assert_nothing_raised { bind.eval("self") }
+end
diff --git a/mrbgems/mruby-proc-ext/mrblib/proc.rb b/mrbgems/mruby-proc-ext/mrblib/proc.rb
index 789b7a3ac..abe9c7944 100644
--- a/mrbgems/mruby-proc-ext/mrblib/proc.rb
+++ b/mrbgems/mruby-proc-ext/mrblib/proc.rb
@@ -39,4 +39,12 @@ class Proc
make_curry.call
end
+ def <<(other)
+ ->(*args, &block) { call(other.call(*args, &block)) }
+ end
+
+ def >>(other)
+ ->(*args, &block) { other.call(call(*args, &block)) }
+ end
+
end
diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c
index 9ce6c1831..9d8a7b037 100644
--- a/mrbgems/mruby-proc-ext/src/proc.c
+++ b/mrbgems/mruby-proc-ext/src/proc.c
@@ -4,29 +4,28 @@
#include <mruby/array.h>
#include <mruby/string.h>
#include <mruby/debug.h>
+#include <mruby/presym.h>
static mrb_value
-mrb_proc_lambda(mrb_state *mrb, mrb_value self)
+mrb_proc_lambda_p(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)
+mrb_value
+mrb_proc_source_location(mrb_state *mrb, struct RProc *p)
{
- struct RProc *p = mrb_proc_ptr(self);
-
if (MRB_PROC_CFUNC_P(p)) {
return mrb_nil_value();
}
else {
- mrb_irep *irep = p->body.irep;
+ const 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);
+ filename = mrb_debug_get_filename(mrb, irep, 0);
+ line = mrb_debug_get_line(mrb, irep, 0);
return (!filename && line == -1)? mrb_nil_value()
: mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line));
@@ -34,25 +33,31 @@ mrb_proc_source_location(mrb_state *mrb, mrb_value self)
}
static mrb_value
+mrb_proc_source_location_m(mrb_state *mrb, mrb_value self)
+{
+ return mrb_proc_source_location(mrb, mrb_proc_ptr(self));
+}
+
+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, "#<Proc:");
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
+ mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
if (!MRB_PROC_CFUNC_P(p)) {
- mrb_irep *irep = p->body.irep;
+ const mrb_irep *irep = p->body.irep;
const char *filename;
int32_t line;
mrb_str_cat_lit(mrb, str, "@");
- filename = mrb_debug_get_filename(irep, 0);
+ filename = mrb_debug_get_filename(mrb, irep, 0);
mrb_str_cat_cstr(mrb, str, filename ? filename : "-");
mrb_str_cat_lit(mrb, str, ":");
- line = mrb_debug_get_line(irep, 0);
+ line = mrb_debug_get_line(mrb, irep, 0);
if (line != -1) {
- str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line));
+ mrb_str_concat(mrb, str, mrb_fixnum_value(line));
}
else {
mrb_str_cat_lit(mrb, str, "-");
@@ -72,10 +77,7 @@ 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");
- }
+ mrb_get_args(mrb, "&!", &blk);
return blk;
}
@@ -94,20 +96,24 @@ static mrb_value
mrb_proc_parameters(mrb_state *mrb, mrb_value self)
{
struct parameters_type {
+ mrb_sym name;
int size;
- const char *name;
} *p, parameters_list [] = {
- {0, "req"},
- {0, "opt"},
- {0, "rest"},
- {0, "req"},
- {0, "block"},
- {0, NULL}
+ {MRB_SYM(req), 0},
+ {MRB_SYM(opt), 0},
+ {MRB_SYM(rest), 0},
+ {MRB_SYM(req), 0},
+ {MRB_SYM(keyrest), 0},
+ {MRB_SYM(block), 0},
+ {MRB_SYM(key), 0},
+ {0, 0}
};
const struct RProc *proc = mrb_proc_ptr(self);
- const struct mrb_irep *irep = proc->body.irep;
+ const struct mrb_irep *irep;
mrb_aspec aspec;
- mrb_value sname, parameters;
+ mrb_value parameters;
+ mrb_value krest = mrb_nil_value();
+ mrb_value block = mrb_nil_value();
int i, j;
int max = -1;
@@ -115,6 +121,7 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
// TODO cfunc aspec is not implemented yet
return mrb_ary_new(mrb);
}
+ irep = proc->body.irep;
if (!irep) {
return mrb_ary_new(mrb);
}
@@ -126,8 +133,8 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
}
if (!MRB_PROC_STRICT_P(proc)) {
- parameters_list[0].name = "opt";
- parameters_list[3].name = "opt";
+ parameters_list[0].name = MRB_SYM(opt);
+ parameters_list[3].name = MRB_SYM(opt);
}
aspec = PEEK_W(irep->iseq+1);
@@ -135,22 +142,24 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
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_list[4].size = MRB_ASPEC_KDICT(aspec);
+ parameters_list[5].size = MRB_ASPEC_BLOCK(aspec);
+ parameters_list[6].size = MRB_ASPEC_KEY(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));
+ mrb_value sname = mrb_symbol_value(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);
+ if (i < max && irep->lv[i]) {
+ mrb_sym sym = irep->lv[i];
+ const char *name = mrb_sym_name(mrb, sym);
switch (name[0]) {
case '*': case '&':
break;
@@ -159,9 +168,17 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
break;
}
}
+ if (p->name == MRB_SYM(block)) {
+ block = a; continue;
+ }
+ if (p->name == MRB_SYM(keyrest)) {
+ krest = a; continue;
+ }
mrb_ary_push(mrb, parameters, a);
}
}
+ if (!mrb_nil_p(krest)) mrb_ary_push(mrb, parameters, krest);
+ if (!mrb_nil_p(block)) mrb_ary_push(mrb, parameters, block);
return parameters;
}
@@ -169,14 +186,14 @@ 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, "lambda?", mrb_proc_lambda_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, p, "source_location", mrb_proc_source_location_m, 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());
+ mrb_define_class_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
}
void
diff --git a/mrbgems/mruby-proc-ext/test/proc.c b/mrbgems/mruby-proc-ext/test/proc.c
index 7072fe2e9..0253f2b60 100644
--- a/mrbgems/mruby-proc-ext/test/proc.c
+++ b/mrbgems/mruby-proc-ext/test/proc.c
@@ -35,7 +35,7 @@ static mrb_value
cfunc_env_get(mrb_state *mrb, mrb_value self)
{
mrb_sym n;
- mrb_value *argv; mrb_int argc;
+ const mrb_value *argv; mrb_int argc;
mrb_method_t m;
struct RProc *p;
mrb_get_args(mrb, "na", &n, &argv, &argc);
diff --git a/mrbgems/mruby-proc-ext/test/proc.rb b/mrbgems/mruby-proc-ext/test/proc.rb
index 037d8d124..06365b7d9 100644
--- a/mrbgems/mruby-proc-ext/test/proc.rb
+++ b/mrbgems/mruby-proc-ext/test/proc.rb
@@ -1,16 +1,41 @@
##
# Proc(Ext) Test
+def enable_debug_info?
+ return @enable_debug_info unless @enable_debug_info == nil
+ begin
+ raise
+ rescue => e
+ @enable_debug_info = !e.backtrace.empty?
+ if(@enable_debug_info && e.backtrace[0].include?("(unknown)"))
+ @enable_debug_info = false
+ end
+ return @enable_debug_info
+ end
+end
+
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
+ skip unless enable_debug_info?
+ file, line = Proc.new{}.source_location
+ assert_equal __FILE__, file
+ assert_equal __LINE__ - 2, line
end
assert('Proc#inspect') do
ins = Proc.new{}.inspect
- assert_kind_of String, ins
+ if enable_debug_info?
+ metas = %w(\\ * ? [ ] { })
+ file = __FILE__.split("").map{|c| metas.include?(c) ? "\\#{c}" : c}.join
+ line = __LINE__ - 4
+ else
+ file = line = "-"
+ end
+ assert_match "#<Proc:0x*@#{file}:#{line}>", ins
+end
+
+assert('Proc#parameters') do
+ parameters = Proc.new{|x,y=42,*other|}.parameters
+ assert_equal [[:opt, :x], [:opt, :y], [:rest, :other]], parameters
end
assert('Proc#lambda?') do
@@ -72,6 +97,19 @@ assert('Kernel#proc') do
end
end
+assert "Proc#<< and Proc#>>" do
+ add3 = ->(n) { n + 3 }
+ mul2 = ->(n) { n * 2 }
+
+ f1 = mul2 << add3
+ assert_kind_of Proc, f1
+ assert_equal 16, f1.call(5)
+
+ f2 = mul2 >> add3
+ assert_kind_of Proc, f2
+ assert_equal 13, f2.call(5)
+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)
diff --git a/mrbgems/mruby-random/src/mt19937ar.c b/mrbgems/mruby-random/src/mt19937ar.c
deleted file mode 100644
index 405bd5c20..000000000
--- a/mrbgems/mruby-random/src/mt19937ar.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
-** 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 <mruby.h>
-#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->mti<N; t->mti++) {
- 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;kk<N-M;kk++) {
- y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK);
- t->mt[kk] = t->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
- }
- for (;kk<N-1;kk++) {
- y = (t->mt[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<N; mti++) {
- mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 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<N-M;kk++) {
- y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
- mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
- }
- for (;kk<N-1;kk++) {
- y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
- mt[kk] = mt[kk+(M-N)] ^ (y >> 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 */
diff --git a/mrbgems/mruby-random/src/mt19937ar.h b/mrbgems/mruby-random/src/mt19937ar.h
deleted file mode 100644
index 7d382320d..000000000
--- a/mrbgems/mruby-random/src/mt19937ar.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-** 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);
diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c
index 5b926a228..9712fbd7e 100644
--- a/mrbgems/mruby-random/src/random.c
+++ b/mrbgems/mruby-random/src/random.c
@@ -9,187 +9,213 @@
#include <mruby/class.h>
#include <mruby/data.h>
#include <mruby/array.h>
-#include "mt19937ar.h"
+#include <mruby/istruct.h>
+#include <mruby/presym.h>
#include <time.h>
-static char const MT_STATE_KEY[] = "$mrb_i_mt_state";
+/* Written in 2019 by David Blackman and Sebastiano Vigna ([email protected])
-static const struct mrb_data_type mt_state_type = {
- MT_STATE_KEY, mrb_free,
-};
+To the extent possible under law, the author has dedicated all copyright
+and related and neighboring rights to this software to the public domain
+worldwide. This software is distributed without any warranty.
-static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self);
-static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self);
+See <https://creativecommons.org/publicdomain/zero/1.0/>. */
+
+#include <stdint.h>
+
+/* This is xoshiro128++ 1.0, one of our 32-bit all-purpose, rock-solid
+ generators. It has excellent speed, a state size (128 bits) that is
+ large enough for mild parallelism, and it passes all tests we are aware
+ of.
+
+ For generating just single-precision (i.e., 32-bit) floating-point
+ numbers, xoshiro128+ is even faster.
+
+ The state must be seeded so that it is not everywhere zero. */
+
+
+#ifdef MRB_32BIT
+# define XORSHIFT96
+# define NSEEDS 3
+# define SEEDPOS 2
+#else
+# define NSEEDS 4
+# define SEEDPOS 0
+#endif
+#define LASTSEED (NSEEDS-1)
+
+typedef struct rand_state {
+ uint32_t seed[NSEEDS];
+} rand_state;
static void
-mt_srand(mt_state *t, unsigned long seed)
+rand_init(rand_state *t)
{
- mrb_random_init_genrand(t, seed);
+ t->seed[0] = 123456789;
+ t->seed[1] = 362436069;
+ t->seed[2] = 521288629;
+#ifndef XORSHIFT96
+ t->seed[3] = 88675123;
+#endif
}
-static unsigned long
-mt_rand(mt_state *t)
+static uint32_t
+rand_seed(rand_state *t, uint32_t seed)
{
- return mrb_random_genrand_int32(t);
+ uint32_t old_seed = t->seed[SEEDPOS];
+ rand_init(t);
+ t->seed[SEEDPOS] = seed;
+ return old_seed;
}
-static double
-mt_rand_real(mt_state *t)
-{
- return mrb_random_genrand_real1(t);
+#ifndef XORSHIFT96
+static inline uint32_t
+rotl(const uint32_t x, int k) {
+ return (x << k) | (x >> (32 - k));
}
+#endif
-static mrb_value
-mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed)
+static uint32_t
+rand_uint32(rand_state *state)
{
- 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));
- }
+#ifdef XORSHIFT96
+ uint32_t *seed = state->seed;
+ uint32_t x = seed[0];
+ uint32_t y = seed[1];
+ uint32_t z = seed[2];
+ uint32_t t;
+
+ t = (x ^ (x << 3)) ^ (y ^ (y >> 19)) ^ (z ^ (z << 6));
+ x = y; y = z; z = t;
+ seed[0] = x;
+ seed[1] = y;
+ seed[2] = z;
+
+ return z;
+#else
+ uint32_t *s = state->seed;
+ const uint32_t result = rotl(s[0] + s[3], 7) + s[0];
+ const uint32_t t = s[1] << 9;
+
+ s[2] ^= s[0];
+ s[3] ^= s[1];
+ s[1] ^= s[2];
+ s[0] ^= s[3];
+
+ s[2] ^= t;
+ s[3] = rotl(s[3], 11);
+
+ return result;
+#endif /* XORSHIFT96 */
}
- mt_srand(t, (unsigned) mrb_fixnum(seed));
-
- return seed;
+#ifndef MRB_NO_FLOAT
+static double
+rand_real(rand_state *t)
+{
+ uint32_t x = rand_uint32(t);
+ return x*(1.0/4294967296.0);
}
+#endif
static mrb_value
-mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max)
+random_rand(mrb_state *mrb, rand_state *t, mrb_int 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));
+ if (max == 0) {
+#ifndef MRB_NO_FLOAT
+ return mrb_float_value(mrb, rand_real(t));
+#else
+ max = 100;
+#endif
}
+ return mrb_int_value(mrb, rand_uint32(t) % max);
+}
- return value;
+static mrb_int
+random_rand_i(mrb_state *mrb, rand_state *t, mrb_int max)
+{
+ return rand_uint32(t) % max;
}
-static mrb_value
+static mrb_int
get_opt(mrb_state* mrb)
{
- mrb_value arg;
+ mrb_int arg;
- arg = mrb_nil_value();
- mrb_get_args(mrb, "|o", &arg);
-
- if (!mrb_nil_p(arg)) {
- arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
- if (mrb_nil_p(arg)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
- }
- if (mrb_fixnum(arg) < 0) {
- arg = mrb_fixnum_value(0 - mrb_fixnum(arg));
- }
+ arg = 0;
+ mrb_get_args(mrb, "|i", &arg);
+ if (arg < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument");
}
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 void
+random_check(mrb_state *mrb, mrb_value random) {
+ struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Random));
+ if (!mrb_obj_is_kind_of(mrb, random, c) || !mrb_istruct_p(random)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Random instance required");
+ }
}
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);
+random_default(mrb_state *mrb) {
+ struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Random));
+ mrb_value d = mrb_const_get(mrb, mrb_obj_value(c), MRB_SYM(DEFAULT));
+ if (!mrb_obj_is_kind_of(mrb, d, c)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Random::DEFAULT replaced");
+ }
+ return d;
}
-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);
-}
+#define random_ptr(v) (rand_state*)mrb_istruct_ptr(v)
+#define random_default_state(mrb) random_ptr(random_default(mrb))
static mrb_value
-mrb_random_init(mrb_state *mrb, mrb_value self)
+random_m_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;
+ mrb_int seed;
+ rand_state *t;
- seed = mrb_random_mt_srand(mrb, t, seed);
- if (mrb_nil_p(seed)) {
- t->has_seed = FALSE;
+ t = random_ptr(self);
+ if (mrb_get_args(mrb, "|i", &seed) == 0) {
+ rand_init(t);
}
else {
- mrb_assert(mrb_fixnum_p(seed));
- t->has_seed = TRUE;
- t->seed = mrb_fixnum(seed);
+ rand_seed(t, (uint32_t)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)
+random_m_rand(mrb_state *mrb, mrb_value self)
{
- mrb_value max;
- mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
+ mrb_int max;
+ rand_state *t = random_ptr(self);
max = get_opt(mrb);
- mrb_random_rand_seed(mrb, t);
- return mrb_random_mt_rand(mrb, t, max);
+ return random_rand(mrb, t, max);
}
static mrb_value
-mrb_random_srand(mrb_state *mrb, mrb_value self)
+random_m_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;
+ uint32_t seed;
+ uint32_t old_seed;
+ mrb_int i;
+ rand_state *t = random_ptr(self);
+
+ if (mrb_get_args(mrb, "|i", &i) == 0) {
+ seed = (uint32_t)time(NULL) + rand_uint32(t);
}
else {
- mrb_assert(mrb_fixnum_p(seed));
- t->has_seed = TRUE;
- t->seed = mrb_fixnum(seed);
+ seed = (uint32_t)i;
}
+ old_seed = rand_seed(t, seed);
- return old_seed;
+ return mrb_int_value(mrb, (mrb_int)old_seed);
}
/*
@@ -202,26 +228,56 @@ mrb_random_srand(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
{
- mrb_int i;
- mt_state *random = NULL;
+ mrb_int i, max;
+ mrb_value r = mrb_nil_value();
+ rand_state *random;
+
+ /*
+ * MSC compiler bug generating invalid instructions with optimization
+ * enabled. MSC errantly uses a hardcoded value with optimizations on
+ * when using a fixed value from a union.
+ * Creating a temp volatile variable and reassigning back to the original
+ * value tricks the compiler to not perform this optimization;
+ */
+#if defined _MSC_VER && _MSC_VER >= 1923
+ /* C++ will not cast away volatile easily, so we cannot do something like
+ * volatile mrb_value rr = r; r = (mrb_value)rr; with C++.
+ * That cast does work with C.
+ * We also have to trick the compiler to not optimize away the const_cast entirely
+ * by creating and manipulating an intermediate volatile pointer.
+ */
+ volatile mrb_value *v_r;
+ volatile mrb_int ii;
+ mrb_value *p_r;
+ v_r = &r;
+ ii = 2;
+ v_r = v_r + 2;
+#if defined __cplusplus
+ p_r = const_cast<mrb_value*>(v_r - ii);
+#else
+ p_r = (mrb_value*)v_r - ii;
+#endif
+ r = *p_r;
+#endif
if (RARRAY_LEN(ary) > 1) {
- mrb_get_args(mrb, "|d", &random, &mt_state_type);
+ mrb_get_args(mrb, "|o", &r);
- if (random == NULL) {
- random = get_random_state(mrb);
+ if (mrb_nil_p(r)) {
+ random = random_default_state(mrb);
+ }
+ else {
+ random_check(mrb, r);
+ random = random_ptr(r);
}
- mrb_random_rand_seed(mrb, random);
-
mrb_ary_modify(mrb, mrb_ary_ptr(ary));
-
+ max = RARRAY_LEN(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))));
+ j = random_rand_i(mrb, random, max);
tmp = ptr[i];
ptr[i] = ptr[j];
@@ -268,15 +324,18 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
{
mrb_int n = 0;
mrb_bool given;
- mt_state *random = NULL;
+ mrb_value r = mrb_nil_value();
+ rand_state *random;
mrb_int len;
- mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type);
- if (random == NULL) {
- random = get_random_state(mrb);
+ mrb_get_args(mrb, "|i?o", &n, &given, &r);
+ if (mrb_nil_p(r)) {
+ random = random_default_state(mrb);
+ }
+ else {
+ random_check(mrb, r);
+ random = random_ptr(r);
}
- mrb_random_rand_seed(mrb, random);
- mt_rand(random);
len = RARRAY_LEN(ary);
if (!given) { /* pick one element */
switch (len) {
@@ -285,7 +344,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
case 1:
return RARRAY_PTR(ary)[0];
default:
- return RARRAY_PTR(ary)[mt_rand(random) % len];
+ return RARRAY_PTR(ary)[rand_uint32(random) % len];
}
}
else {
@@ -300,47 +359,65 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
for (;;) {
retry:
- r = mt_rand(random) % len;
+ r = (mrb_int)(rand_uint32(random) % len);
for (j=0; j<i; j++) {
- if (mrb_fixnum(RARRAY_PTR(result)[j]) == r) {
+ if (mrb_integer(RARRAY_PTR(result)[j]) == r) {
goto retry; /* retry if duplicate */
}
}
break;
}
- mrb_ary_push(mrb, result, mrb_fixnum_value(r));
+ mrb_ary_push(mrb, result, mrb_int_value(mrb, r));
}
for (i=0; i<n; i++) {
- mrb_ary_set(mrb, result, i, RARRAY_PTR(ary)[mrb_fixnum(RARRAY_PTR(result)[i])]);
+ mrb_int idx = mrb_integer(RARRAY_PTR(result)[i]);
+ mrb_value elem = RARRAY_PTR(ary)[idx];
+ mrb_ary_set(mrb, result, i, elem);
}
return result;
}
}
+static mrb_value
+random_f_rand(mrb_state *mrb, mrb_value self)
+{
+ rand_state *t = random_default_state(mrb);
+ return random_rand(mrb, t, get_opt(mrb));
+}
+
+static mrb_value
+random_f_srand(mrb_state *mrb, mrb_value self)
+{
+ mrb_value random = random_default(mrb);
+ return random_m_srand(mrb, random);
+}
+
void mrb_mruby_random_gem_init(mrb_state *mrb)
{
struct RClass *random;
struct RClass *array = mrb->array_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));
+ mrb_static_assert1(sizeof(rand_state) <= ISTRUCT_DATA_SIZE);
+
+ mrb_define_method(mrb, mrb->kernel_module, "rand", random_f_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, mrb->kernel_module, "srand", random_f_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_SET_INSTANCE_TT(random, MRB_TT_ISTRUCT);
+ mrb_define_class_method(mrb, random, "rand", random_f_rand, MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, random, "srand", random_f_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, random, "initialize", random_m_init, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "rand", random_m_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "srand", random_m_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_const_set(mrb, mrb_obj_value(random), MRB_SYM(DEFAULT),
mrb_obj_new(mrb, random, 0, NULL));
}
diff --git a/mrbgems/mruby-random/src/random.h b/mrbgems/mruby-random/src/random.h
deleted file mode 100644
index a4785ae5a..000000000
--- a/mrbgems/mruby-random/src/random.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-** 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
diff --git a/mrbgems/mruby-random/test/random.rb b/mrbgems/mruby-random/test/random.rb
index 1c59be3a6..37583c5d3 100644
--- a/mrbgems/mruby-random/test/random.rb
+++ b/mrbgems/mruby-random/test/random.rb
@@ -1,48 +1,59 @@
##
# Random Test
-assert("Random#srand") do
+assert("Random.new") do
r1 = Random.new(123)
r2 = Random.new(123)
- r1.rand == r2.rand
+ r3 = Random.new(124)
+ assert_equal(r1.rand, r2.rand)
+ assert_not_equal(r1.rand, r3.rand)
end
-assert("Kernel::srand") do
+assert("Kernel.srand") do
srand(234)
r1 = rand
srand(234)
r2 = rand
- r1 == r2
+ srand(235)
+ r3 = rand
+ assert_equal(r1, r2)
+ assert_not_equal(r1, r3)
end
-assert("Random::srand") do
+assert("Random.srand") do
Random.srand(345)
r1 = rand
srand(345)
r2 = Random.rand
- r1 == r2
+ Random.srand(346)
+ r3 = rand
+ assert_equal(r1, r2)
+ assert_not_equal(r1, r3)
end
-assert("fixnum") do
- rand(3).class == Fixnum
-end
-
-assert("float") do
- rand.class == Float
+assert("return class of Kernel.rand") do
+ assert_kind_of(Integer, rand(3))
+ assert_kind_of(Integer, rand(1.5))
+ skip unless Object.const_defined?(:Float)
+ assert_kind_of(Float, rand)
+ assert_kind_of(Float, rand(0.5))
end
assert("Array#shuffle") do
- ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary = orig.dup
shuffled = ary.shuffle
-
- ary == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and shuffled != ary and 10.times { |x| ary.include? x }
+ assert_equal(orig, ary)
+ assert_not_equal(ary, shuffled)
+ assert_equal(orig, shuffled.sort)
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 }
+ orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary = orig.dup
+ assert_same(ary, ary.shuffle!)
+ assert_not_equal(orig, ary)
+ assert_equal(orig, ary.sort)
end
assert("Array#shuffle(random)") do
@@ -52,12 +63,12 @@ assert("Array#shuffle(random)") do
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
+ ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ shuffled1 = ary.shuffle Random.new 345
+ shuffled2 = ary.shuffle Random.new 345
+ shuffled3 = ary.shuffle Random.new 346
+ assert_equal(shuffled1, shuffled2)
+ assert_not_equal(shuffled1, shuffled3)
end
assert('Array#shuffle!(random)') do
@@ -71,18 +82,42 @@ assert('Array#shuffle!(random)') do
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
+ ary3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary3.shuffle! Random.new 346
+ assert_equal(ary1, ary2)
+ assert_not_equal(ary1, ary3)
end
-assert('Array#sample checks input length after reading arguments') do
- $ary = [1, 2, 3]
- class ArrayChange
- def to_i
- $ary << 4
- 4
+assert('Array#sample') do
+ 100.times do
+ assert_include([0, 1, 2], [2, 1, 0].sample)
+ [2, 1, 0].sample(2).each { |sample| assert_include([0, 1, 2], sample) }
+ h = {}
+ (1..10).to_a.sample(7).each do |sample|
+ assert_not_include(h, sample)
+ h[sample] = true
end
end
- assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
+ assert_nil([].sample)
+ assert_equal([], [].sample(1))
+ assert_equal([], [2, 1].sample(0))
+ assert_raise(TypeError) { [2, 1].sample(true) }
+ assert_raise(ArgumentError) { [2, 1].sample(-1) }
+end
+
+assert('Array#sample(random)') do
+ assert_raise(TypeError) do
+ # this will cause an exception due to the wrong argument
+ [1, 2].sample(2, "Not a Random instance")
+ end
+
+ # verify that the same seed causes the same results
+ ary = (1..10).to_a
+ srand(15)
+ samples1 = ary.sample(4)
+ samples2 = ary.sample(4, Random.new(15))
+ samples3 = ary.sample(4, Random.new(16))
+ assert_equal(samples1, samples2)
+ assert_not_equal(samples1, samples3)
end
diff --git a/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb
index e5d1fb079..7fe502c9d 100644
--- a/mrbgems/mruby-range-ext/mrblib/range.rb
+++ b/mrbgems/mruby-range-ext/mrblib/range.rb
@@ -11,14 +11,12 @@ class Range
# (10..20).first(3) #=> [10, 11, 12]
#
def first(*args)
+ raise RangeError, "cannot get the first element of beginless range" if self.begin.nil?
return self.begin if args.empty?
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
nv = args[0]
- raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
- raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
- n = nv.to_int
- raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
+ n = nv.__to_int
raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
ary = []
each do |i|
@@ -28,4 +26,74 @@ class Range
end
ary
end
+
+ ##
+ # 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]
+ def last(*args)
+ raise RangeError, "cannot get the last element of endless range" if self.end.nil?
+ return self.end 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
+ return self.to_a.last(nv)
+ end
+
+ def max(&block)
+ val = self.begin
+ last = self.end
+ return super if block
+
+ raise RangeError, "cannot get the maximum of endless range" if last.nil?
+
+ # fast path for numerics
+ if val.kind_of?(Numeric) && last.kind_of?(Numeric)
+ raise TypeError if exclude_end? && !last.kind_of?(Integer)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ max = last
+ max -= 1 if exclude_end?
+ return max
+ end
+
+ # delegate to Enumerable
+ super
+ end
+
+ def min(&block)
+ val = self.begin
+ last = self.end
+ if block
+ raise RangeError, "cannot get the minimum of endless range with custom comparison method" if last.nil?
+ return super
+ end
+ return val if last.nil?
+
+ # fast path for numerics
+ if val.kind_of?(Numeric) && last.kind_of?(Numeric)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ min = val
+ return min
+ end
+
+ # delegate to Enumerable
+ super
+ end
end
diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c
index aca71cc01..2d021f31f 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -5,24 +5,16 @@
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;
- }
+ mrb_int n = mrb_cmp(mrb, a, b);
+ if (n == 0 || n == -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;
+ return mrb_cmp(mrb, a, b) == -1;
}
/*
@@ -40,19 +32,20 @@ r_lt(mrb_state *mrb, mrb_value a, mrb_value b)
* ("a".."z").cover?("cc") #=> true
*/
static mrb_value
-mrb_range_cover(mrb_state *mrb, mrb_value range)
+range_cover(mrb_state *mrb, mrb_value range)
{
- mrb_value val;
struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value val = mrb_get_arg1(mrb);
mrb_value beg, end;
- mrb_get_args(mrb, "o", &val);
-
- beg = r->edges->beg;
- end = r->edges->end;
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
if (r_le(mrb, beg, val)) {
- if (r->excl) {
+ if (mrb_nil_p(end)) {
+ return mrb_true_value();
+ }
+ if (RANGE_EXCL(r)) {
if (r_lt(mrb, val, end))
return mrb_true_value();
}
@@ -67,37 +60,6 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
/*
* 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
@@ -107,8 +69,9 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
* ('a'..'z').size #=> nil
*/
+#ifndef MRB_NO_FLOAT
static mrb_value
-mrb_range_size(mrb_state *mrb, mrb_value range)
+range_size(mrb_state *mrb, mrb_value range)
{
struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
@@ -116,11 +79,15 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
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);
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ if ((mrb_integer_p(beg) || mrb_float_p(beg)) && mrb_nil_p(end)) {
+ return mrb_float_value(mrb, INFINITY);
+ }
+
+ excl = RANGE_EXCL(r);
+ if (mrb_integer_p(beg)) {
+ beg_f = (mrb_float)mrb_integer(beg);
}
else if (mrb_float_p(beg)) {
beg_f = mrb_float(beg);
@@ -128,8 +95,8 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
else {
num_p = FALSE;
}
- if (mrb_fixnum_p(end)) {
- end_f = (mrb_float)mrb_fixnum(end);
+ if (mrb_integer_p(end)) {
+ end_f = (mrb_float)mrb_integer(end);
}
else if (mrb_float_p(end)) {
end_f = mrb_float(end);
@@ -159,15 +126,40 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
}
return mrb_nil_value();
}
+#else
+static mrb_value
+range_size(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value beg, end;
+ mrb_int excl;
+
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ if (mrb_integer_p(beg) && mrb_nil_p(end)) {
+ return mrb_nil_value();
+ }
+
+ excl = RANGE_EXCL(r) ? 0 : 1;
+
+ if (mrb_integer_p(beg) && mrb_integer_p(end)) {
+ mrb_int a = mrb_integer(beg);
+ mrb_int b = mrb_integer(end);
+ mrb_int c = b - a + excl;
+
+ return mrb_int_value(mrb, c);
+ }
+ return mrb_nil_value();
+}
+#endif /* MRB_NO_FLOAT */
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());
+ mrb_define_method(mrb, s, "cover?", range_cover, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "size", range_size, MRB_ARGS_NONE());
}
void
diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb
index efcbdabe4..863c619c8 100644
--- a/mrbgems/mruby-range-ext/test/range.rb
+++ b/mrbgems/mruby-range-ext/test/range.rb
@@ -5,17 +5,27 @@ assert('Range#cover?') do
assert_true ("a".."z").cover?("c")
assert_true !("a".."z").cover?("5")
assert_true ("a".."z").cover?("cc")
+ assert_true ("a"..).cover?("c")
+ assert_false ("a"..).cover?("5")
+ assert_true ("a"..).cover?("cc")
end
assert('Range#first') do
assert_equal 10, (10..20).first
assert_equal [10, 11, 12], (10..20).first(3)
+ assert_equal 10, (10..).first
+ assert_equal [10, 11, 12], (10..).first(3)
+ assert_raise(RangeError) { (..1).first }
+
+ skip unless Object.const_defined?(:Float)
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_raise(RangeError) { (10..).last }
+ assert_raise(RangeError) { (10...).last }
assert_equal [18, 19, 20], (10..20).last(3)
assert_equal [17, 18, 19], (10...20).last(3)
end
@@ -23,10 +33,134 @@ end
assert('Range#size') do
assert_equal 42, (1..42).size
assert_equal 41, (1...42).size
+ assert_nil ('a'..'z').size
+ assert_nil ('a'..).size
+
+ assert_nil (1..).size unless Object.const_defined?(:Float)
+
+ skip unless Object.const_defined?(:Float)
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
+
+ assert_equal Float::INFINITY, (1..).size
+ assert_equal Float::INFINITY, (1...).size
+ assert_equal Float::INFINITY, (1.0..).size
+end
+
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 10, (1..10).max
+ assert_equal 9, (1...10).max
+ assert_equal 536870911, (0...2**29).max
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (100..10).max
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).max
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).max
+
+ # raises RangeError when called on an endless range
+ assert_raise(RangeError) { (10..).max }
+ assert_raise(RangeError) { (10...).max }
+
+ skip unless Object.const_defined?(:Float)
+
+ # returns the maximum value in the Float range when called with no arguments
+ assert_equal 908.1111, (303.20..908.1111).max
+
+ # raises TypeError when called on an exclusive range and a non Integer value
+ assert_raise(TypeError) { (303.20...908.1111).max }
+
+ # returns nil when the endpoint is less than the start point in a Float range
+ assert_equal nil, (3003.20..908.1111).max
+end
+
+assert('Range#max given a block') do
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).max do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).max do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 2], [4, 3], [5, 4]], acc
+
+ # returns the element the block determines to be the maximum
+ assert_equal 1, ((1..3).max { |_a, _b| -3 })
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ((100..10).max { |x, y| x <=> y })
+ assert_equal nil, ((5...5).max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 1, (1..10).min
+ assert_equal 1, (1...10).min
+ assert_equal 1, (1..).min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (100..10).min
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).min
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).min
+
+ skip unless Object.const_defined?(:Float)
+
+ # returns the minimum value in the Float range when called with no arguments
+ assert_equal 303.20, (303.20..908.1111).min
+ assert_equal 1, (1.0..).min
+
+ # returns nil when the start point is greater than the endpoint in a Float range
+ assert_equal nil, (3003.20..908.1111).min
+end
+
+assert('Range#min given a block') do
+ # raise when called with a block in endless range
+ assert_raise(RangeError) { (1..).min{} }
+
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).min do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).min do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 1], [4, 1], [5, 1]], acc
+
+ # returns the element the block determines to be the minimum
+ assert_equal 3, ((1..3).min { |_a, _b| -3 })
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ((100..10).min { |x, y| x <=> y })
+ assert_equal nil, ((5...5).min { |x, y| x <=> y })
end
diff --git a/mrbgems/mruby-rational/mrbgem.rake b/mrbgems/mruby-rational/mrbgem.rake
new file mode 100644
index 000000000..534114862
--- /dev/null
+++ b/mrbgems/mruby-rational/mrbgem.rake
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-rational') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Rational class'
+ spec.build.defines << "MRB_USE_RATIONAL"
+ spec.add_test_dependency('mruby-complex')
+end
diff --git a/mrbgems/mruby-rational/mrblib/rational.rb b/mrbgems/mruby-rational/mrblib/rational.rb
new file mode 100644
index 000000000..ebe6829b0
--- /dev/null
+++ b/mrbgems/mruby-rational/mrblib/rational.rb
@@ -0,0 +1,15 @@
+class Rational < Numeric
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{numerator}/#{denominator}"
+ end
+end
+
+class Numeric
+ def to_r
+ Rational(self, 1)
+ end
+end
diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c
new file mode 100644
index 000000000..7d099b1e2
--- /dev/null
+++ b/mrbgems/mruby-rational/src/rational.c
@@ -0,0 +1,740 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/presym.h>
+
+struct mrb_rational {
+ mrb_int numerator;
+ mrb_int denominator;
+};
+
+#if defined(MRB_INT64) && defined(MRB_32BIT)
+struct RRational {
+ MRB_OBJECT_HEADER;
+ struct mrb_rational *p;
+};
+
+static struct mrb_rational*
+rational_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct RRational *r = (struct RRational*)mrb_obj_ptr(v);
+
+ if (!r->p) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational");
+ }
+ return r->p;
+}
+#else
+#define RATIONAL_INLINE
+struct RRational {
+ MRB_OBJECT_HEADER;
+ struct mrb_rational r;
+};
+#define rational_ptr(mrb, v) (&((struct RRational*)mrb_obj_ptr(v))->r)
+#endif
+
+static struct RBasic*
+rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
+{
+ struct RRational *s;
+ s = MRB_OBJ_ALLOC(mrb, MRB_TT_RATIONAL, c);
+#ifdef RATIONAL_INLINE
+ *p = &s->r;
+#else
+ *p = s->p = (struct mrb_rational*)mrb_malloc(mrb, sizeof(struct mrb_rational));
+#endif
+ return (struct RBasic*)s;
+}
+
+static mrb_value
+rational_numerator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ return mrb_int_value(mrb, p->numerator);
+}
+
+static mrb_value
+rational_denominator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ return mrb_int_value(mrb, p->denominator);
+}
+
+static void
+rat_overflow(mrb_state *mrb)
+{
+ mrb_raise(mrb, E_RANGE_ERROR, "integer overflow in rational");
+}
+
+static void
+rat_zerodiv(mrb_state *mrb)
+{
+ mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0 in rational");
+}
+
+mrb_value
+mrb_rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator)
+{
+ struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational));
+ struct mrb_rational *p;
+ struct RBasic *rat;
+
+ if (denominator == 0) {
+ rat_zerodiv(mrb);
+ }
+ if (denominator < 0) {
+ if (numerator == MRB_INT_MIN || denominator == MRB_INT_MIN) {
+ rat_overflow(mrb);
+ }
+ numerator *= -1;
+ denominator *= -1;
+ }
+ rat = rational_alloc(mrb, c, &p);
+ p->numerator = numerator;
+ p->denominator = denominator;
+ MRB_SET_FROZEN_FLAG(rat);
+ return mrb_obj_value(rat);
+}
+
+#define rational_new(mrb,n,d) mrb_rational_new(mrb, n, d)
+
+inline static mrb_int
+i_gcd(mrb_int x, mrb_int y)
+{
+ mrb_uint u, v, t;
+ int shift;
+
+ if (x < 0)
+ x = -x;
+ if (y < 0)
+ y = -y;
+
+ if (x == 0)
+ return y;
+ if (y == 0)
+ return x;
+
+ u = (mrb_uint)x;
+ v = (mrb_uint)y;
+ for (shift = 0; ((u | v) & 1) == 0; ++shift) {
+ u >>= 1;
+ v >>= 1;
+ }
+
+ while ((u & 1) == 0)
+ u >>= 1;
+
+ do {
+ while ((v & 1) == 0)
+ v >>= 1;
+
+ if (u > v) {
+ t = v;
+ v = u;
+ u = t;
+ }
+ v = v - u;
+ } while (v != 0);
+
+ return (mrb_int)(u << shift);
+}
+
+static mrb_value
+rational_new_i(mrb_state *mrb, mrb_int n, mrb_int d)
+{
+ mrb_int a;
+
+ if (d == 0) {
+ rat_zerodiv(mrb);
+ }
+ if (n == MRB_INT_MIN || d == MRB_INT_MIN) {
+ rat_overflow(mrb);
+ }
+ a = i_gcd(n, d);
+ return rational_new(mrb, n/a, d/a);
+}
+
+#ifndef MRB_NO_FLOAT
+#include <math.h>
+
+#if defined(MRB_INT32) || defined(MRB_USE_FLOAT32)
+#define frexp_rat(x,exp) frexpf((float)x, exp)
+#define ldexp_rat(x,exp) ldexpf((float)x, exp)
+#define RAT_MANT_DIG FLT_MANT_DIG
+#define RAT_INT_LIMIT 30
+#define RAT_HUGE_VAL HUGE_VALF
+#else
+#define frexp_rat frexp
+#define ldexp_rat ldexp
+#define RAT_MANT_DIG DBL_MANT_DIG
+#define RAT_INT_LIMIT 62
+#define RAT_HUGE_VAL HUGE_VAL
+#endif
+
+static void
+float_decode_internal(mrb_state *mrb, mrb_float f, mrb_float *rf, int *n)
+{
+ f = (mrb_float)frexp_rat(f, n);
+ if (isinf(f)) rat_overflow(mrb);
+ f = (mrb_float)ldexp_rat(f, RAT_MANT_DIG);
+ *n -= RAT_MANT_DIG;
+ *rf = f;
+}
+
+void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
+
+static mrb_value
+rational_new_f(mrb_state *mrb, mrb_float f0)
+{
+ mrb_float f;
+ int n;
+
+ mrb_check_num_exact(mrb, f0);
+ float_decode_internal(mrb, f0, &f, &n);
+#if FLT_RADIX == 2
+ if (n == 0)
+ return rational_new(mrb, (mrb_int)f, 1);
+ if (n > 0) {
+ f = ldexp_rat(f, n);
+ if (f == RAT_HUGE_VAL || f > (mrb_float)MRB_INT_MAX) {
+ rat_overflow(mrb);
+ }
+ return rational_new(mrb, (mrb_uint)f, 1);
+ }
+ if (n < -RAT_INT_LIMIT) {
+ f = ldexp_rat(f, n+RAT_INT_LIMIT);
+ n = RAT_INT_LIMIT;
+ }
+ else {
+ n = -n;
+ }
+ return rational_new_i(mrb, (mrb_int)f, ((mrb_int)1)<<n);
+#else
+ mrb_int pow = 1;
+ if (n < 0) {
+ n = -n;
+ while (n > RAT_INT_LIMIT) {
+ f /= 2;
+ n--;
+ }
+ while (n--) {
+ pow *= FLT_RADIX;
+ }
+ return rational_new_i(mrb, f, pow);
+ }
+ else {
+ while (n--) {
+ if (MRB_INT_MAX/FLT_RADIX < pow) {
+ rat_overflow(mrb);
+ }
+ pow *= FLT_RADIX;
+ }
+ return rational_new(mrb, (mrb_int)f*pow, 1);
+ }
+#endif
+}
+#endif
+
+static mrb_value
+rational_s_new(mrb_state *mrb, mrb_value self)
+{
+ mrb_int numerator, denominator;
+
+#ifdef MRB_NO_FLOAT
+ mrb_get_args(mrb, "ii", &numerator, &denominator);
+#else
+
+ mrb_value numv, denomv;
+
+ mrb_get_args(mrb, "oo", &numv, &denomv);
+ if (mrb_integer_p(numv)) {
+ numerator = mrb_integer(numv);
+
+ if (mrb_integer_p(denomv)) {
+ denominator = mrb_integer(denomv);
+ }
+ else {
+ mrb_float numf = (mrb_float)numerator;
+ mrb_float denomf = mrb_as_float(mrb, denomv);
+
+ return rational_new_f(mrb, numf/denomf);
+ }
+ }
+ else {
+ mrb_float numf = mrb_as_float(mrb, numv);
+ mrb_float denomf;
+
+ if (mrb_integer_p(denomv)) {
+ denomf = (mrb_float)mrb_integer(denomv);
+ }
+ else {
+ denomf = mrb_as_float(mrb, denomv);
+ }
+ return rational_new_f(mrb, numf/denomf);
+ }
+#endif
+ return rational_new(mrb, numerator, denominator);
+}
+
+#ifndef MRB_NO_FLOAT
+static mrb_float
+rat_float(struct mrb_rational *p)
+{
+ mrb_float f;
+
+ if (p->denominator == 0.0) {
+ f = INFINITY;
+ }
+ else {
+ f = (mrb_float)p->numerator / (mrb_float)p->denominator;
+ }
+
+ return f;
+}
+
+static mrb_value
+rational_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ return mrb_float_value(mrb, rat_float(p));
+}
+#endif
+
+static mrb_value
+rational_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ if (p->denominator == 0) {
+ rat_zerodiv(mrb);
+ }
+ return mrb_int_value(mrb, p->numerator / p->denominator);
+}
+
+static mrb_value
+rational_to_r(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+static mrb_value
+rational_negative_p(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ if (p->numerator < 0) {
+ return mrb_true_value();
+ }
+ return mrb_false_value();
+}
+
+static mrb_value
+fix_to_r(mrb_state *mrb, mrb_value self)
+{
+ return rational_new(mrb, mrb_integer(self), 1);
+}
+
+static mrb_value
+rational_m(mrb_state *mrb, mrb_value self)
+{
+#ifdef MRB_NO_FLOAT
+ mrb_int n, d = 1;
+ mrb_get_args(mrb, "i|i", &n, &d);
+ return rational_new_i(mrb, n, d);
+#else
+ mrb_value a, b = mrb_fixnum_value(1);
+ mrb_get_args(mrb, "o|o", &a, &b);
+ if (mrb_integer_p(a) && mrb_integer_p(b)) {
+ return rational_new_i(mrb, mrb_integer(a), mrb_integer(b));
+ }
+ else {
+ mrb_float x = mrb_as_float(mrb, a);
+ mrb_float y = mrb_as_float(mrb, b);
+ return rational_new_f(mrb, x/y);
+ }
+#endif
+}
+
+static mrb_value
+rational_eq(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ struct mrb_rational *p1 = rational_ptr(mrb, x);
+ mrb_bool result;
+
+ switch (mrb_type(y)) {
+ case MRB_TT_INTEGER:
+ if (p1->denominator != 1) return mrb_false_value();
+ result = p1->numerator == mrb_integer(y);
+ break;
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ result = ((double)p1->numerator/p1->denominator) == mrb_float(y);
+ break;
+#endif
+ case MRB_TT_RATIONAL:
+ {
+ struct mrb_rational *p2 = rational_ptr(mrb, y);
+ mrb_int a, b;
+
+ if (p1->numerator == p2->numerator && p1->denominator == p2->denominator) {
+ return mrb_true_value();
+ }
+ if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a) ||
+ mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) {
+#ifdef MRB_NO_FLOAT
+ rat_overflow(mrb);
+#else
+ result = (double)p1->numerator*p2->denominator == (double)p2->numerator*p2->denominator;
+ break;
+#endif
+ }
+ result = a == b;
+ break;
+ }
+
+#ifdef MRB_USE_COMPLEX
+ case MRB_TT_COMPLEX:
+ {
+ mrb_bool mrb_complex_eq(mrb_state *mrb, mrb_value, mrb_value);
+ result = mrb_complex_eq(mrb, y, x);
+ break;
+ }
+#endif
+ default:
+ result = mrb_equal(mrb, y, x);
+ break;
+ }
+ return mrb_bool_value(result);
+}
+
+#ifndef MRB_NO_FLOAT
+mrb_value mrb_complex_new(mrb_state *, mrb_float, mrb_float);
+#endif
+
+static mrb_value
+rational_cmp(mrb_state *mrb, mrb_value x)
+{
+ struct mrb_rational *p1 = rational_ptr(mrb, x);
+ mrb_value y = mrb_get_arg1(mrb);
+
+ switch(mrb_type(y)) {
+ case MRB_TT_RATIONAL:
+ {
+ struct mrb_rational *p2 = rational_ptr(mrb, y);
+ mrb_int a, b;
+
+ if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a) ||
+ mrb_int_mul_overflow(p1->denominator, p2->numerator, &b)) {
+ return mrb_nil_value();
+ }
+ if (a > b)
+ return mrb_fixnum_value(1);
+ else if (a < b)
+ return mrb_fixnum_value(-1);
+ return mrb_fixnum_value(0);
+ }
+ case MRB_TT_INTEGER:
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ {
+ mrb_float a = rat_float(p1), b = mrb_as_float(mrb, y);
+ if (a > b)
+ return mrb_fixnum_value(1);
+ else if (a < b)
+ return mrb_fixnum_value(-1);
+ return mrb_fixnum_value(0);
+ }
+#else
+ {
+ mrb_int a = p1->numerator, b;
+ if (mrb_int_mul_overflow(p1->denominator, mrb_integer(y), &b)) {
+ return mrb_nil_value();
+ }
+ if (a > b)
+ return mrb_fixnum_value(1);
+ else if (a < b)
+ return mrb_fixnum_value(-1);
+ return mrb_fixnum_value(0);
+ }
+#endif
+#ifdef MRB_USE_COMPLEX
+ case MRB_TT_COMPLEX:
+ x = mrb_complex_new(mrb, rat_float(p1), 0);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(cmp), 1, y);
+#endif
+ default:
+ x = mrb_funcall_id(mrb, y, MRB_OPSYM(cmp), 1, x);
+ if (mrb_integer_p(x)) {
+ mrb_int z = mrb_integer(x);
+ return mrb_fixnum_value(-z);
+ }
+ return mrb_nil_value();
+ }
+}
+
+static mrb_value
+rational_minus(mrb_state *mrb, mrb_value x)
+{
+ struct mrb_rational *p = rational_ptr(mrb, x);
+ mrb_int n = p->numerator;
+ if (n == MRB_INT_MIN) rat_overflow(mrb);
+ return rational_new(mrb, -n, p->denominator);
+}
+
+static mrb_value
+rational_add(mrb_state *mrb, mrb_value x)
+{
+ struct mrb_rational *p1 = rational_ptr(mrb, x);
+ mrb_value y = mrb_get_arg1(mrb);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_INTEGER:
+ {
+ mrb_int z = mrb_integer(y);
+ if (mrb_int_mul_overflow(z, p1->denominator, &z)) rat_overflow(mrb);
+ if (mrb_int_add_overflow(p1->numerator, z, &z)) rat_overflow(mrb);
+ return rational_new_i(mrb, z, p1->denominator);
+ }
+ case MRB_TT_RATIONAL:
+ {
+ struct mrb_rational *p2 = rational_ptr(mrb, y);
+ mrb_int a, b;
+
+ if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb);
+ if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb);
+ if (mrb_int_add_overflow(a, b, &a)) rat_overflow(mrb);
+ if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb);
+ return rational_new_i(mrb, a, b);
+ }
+
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ {
+ mrb_float z = p1->numerator + mrb_float(y) * p1->denominator;
+ return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
+ }
+#endif
+
+ default:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x);
+ }
+}
+
+static mrb_value
+rational_sub(mrb_state *mrb, mrb_value x)
+{
+ struct mrb_rational *p1 = rational_ptr(mrb, x);
+ mrb_value y = mrb_get_arg1(mrb);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_INTEGER:
+ {
+ mrb_int z = mrb_integer(y);
+ if (mrb_int_mul_overflow(z, p1->denominator, &z)) rat_overflow(mrb);
+ if (mrb_int_sub_overflow(p1->numerator, z, &z)) rat_overflow(mrb);
+ return rational_new_i(mrb, z, p1->denominator);
+ }
+ case MRB_TT_RATIONAL:
+ {
+ struct mrb_rational *p2 = rational_ptr(mrb, y);
+ mrb_int a, b;
+
+ if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb);
+ if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb);
+ if (mrb_int_sub_overflow(a, b, &a)) rat_overflow(mrb);
+ if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb);
+ return rational_new_i(mrb, a, b);
+ }
+
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ x = mrb_complex_new(mrb, rat_float(p1), 0);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(sub), 1, y);
+#endif
+
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ default:
+ {
+ mrb_float z = p1->numerator - mrb_as_float(mrb, y) * p1->denominator;
+ return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
+ }
+#else
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer subtraction");
+#endif
+ }
+}
+
+static mrb_value
+rational_mul(mrb_state *mrb, mrb_value x)
+{
+ struct mrb_rational *p1 = rational_ptr(mrb, x);
+ mrb_value y = mrb_get_arg1(mrb);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_INTEGER:
+ {
+ mrb_int z = mrb_integer(y);
+ if (mrb_int_mul_overflow(p1->numerator, z, &z)) rat_overflow(mrb);
+ return rational_new_i(mrb, z, p1->denominator);
+ }
+ case MRB_TT_RATIONAL:
+ {
+ struct mrb_rational *p2 = rational_ptr(mrb, y);
+ mrb_int a, b;
+
+ if (mrb_int_mul_overflow(p1->numerator, p2->numerator, &a)) rat_overflow(mrb);
+ if (mrb_int_mul_overflow(p1->denominator, p2->denominator, &b)) rat_overflow(mrb);
+ return rational_new_i(mrb, a, b);
+ }
+
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ {
+ mrb_float z = p1->numerator * mrb_float(y);
+ return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
+ }
+#endif
+
+ default:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x);
+ }
+}
+
+mrb_value
+mrb_rational_div(mrb_state *mrb, mrb_value x)
+{
+ struct mrb_rational *p1 = rational_ptr(mrb, x);
+ mrb_value y = mrb_get_arg1(mrb);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_INTEGER:
+ {
+ mrb_int z = mrb_integer(y);
+ if (mrb_int_mul_overflow(p1->denominator, z, &z)) rat_overflow(mrb);
+ return rational_new_i(mrb, p1->numerator, z);
+ }
+ case MRB_TT_RATIONAL:
+ {
+ struct mrb_rational *p2 = rational_ptr(mrb, y);
+ mrb_int a, b;
+
+ if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a)) rat_overflow(mrb);
+ if (mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) rat_overflow(mrb);
+ return rational_new_i(mrb, a, b);
+ }
+
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ x = mrb_complex_new(mrb, rat_float(p1), 0);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(div), 1, y);
+#endif
+
+ default:
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ {
+ mrb_float z = mrb_div_float((mrb_float)p1->numerator, mrb_as_float(mrb, y));
+ return mrb_float_value(mrb, mrb_div_float(z, (mrb_float)p1->denominator));
+ }
+#else
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer division");
+#endif
+ }
+}
+
+#define rational_div mrb_rational_div
+mrb_int mrb_div_int(mrb_state *, mrb_int, mrb_int);
+
+#ifndef MRB_USE_COMPLEX
+/* 15.2.8.3.4 */
+/*
+ * redefine Integer#/
+ */
+static mrb_value
+rational_int_div(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a = mrb_integer(x);
+
+ if (mrb_integer_p(y)) {
+ mrb_int div = mrb_div_int(mrb, a, mrb_integer(y));
+ return mrb_int_value(mrb, div);
+ }
+ switch (mrb_type(y)) {
+ case MRB_TT_RATIONAL:
+ return rational_div(mrb, rational_new(mrb, a, 1));
+ default:
+#ifdef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication");
+#else
+ return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
+#endif
+ }
+}
+
+/* 15.2.9.3.19(x) */
+/*
+ * redefine Integer#quo
+ */
+
+static mrb_value
+rational_int_quo(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a = mrb_integer(x);
+
+ if (mrb_integer_p(y)) {
+ return rational_new(mrb, a, mrb_integer(y));
+ }
+ switch (mrb_type(y)) {
+ case MRB_TT_RATIONAL:
+ x = rational_new(mrb, a, 1);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(div), 1, y);
+ default:
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication");
+#else
+ return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
+#endif
+ }
+}
+#endif /* !MRB_USE_COMPLEX */
+
+void mrb_mruby_rational_gem_init(mrb_state *mrb)
+{
+ struct RClass *rat;
+
+ rat = mrb_define_class_id(mrb, MRB_SYM(Rational), mrb_class_get_id(mrb, MRB_SYM(Numeric)));
+ MRB_SET_INSTANCE_TT(rat, MRB_TT_RATIONAL);
+ mrb_undef_class_method(mrb, rat, "new");
+ mrb_define_class_method(mrb, rat, "_new", rational_s_new, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, rat, "numerator", rational_numerator, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "denominator", rational_denominator, MRB_ARGS_NONE());
+#ifndef MRB_NO_FLOAT
+ mrb_define_method(mrb, rat, "to_f", rational_to_f, MRB_ARGS_NONE());
+#endif
+ mrb_define_method(mrb, rat, "to_i", rational_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "to_r", rational_to_r, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "negative?", rational_negative_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "==", rational_eq, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, rat, "<=>", rational_cmp, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, rat, "-@", rational_minus, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "+", rational_add, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, rat, "-", rational_sub, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, rat, "*", rational_mul, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, rat, "/", rational_div, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, rat, "quo", rational_div, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mrb->integer_class, "to_r", fix_to_r, MRB_ARGS_NONE());
+#ifndef MRB_USE_COMPLEX
+ mrb_define_method(mrb, mrb->integer_class, "/", rational_int_div, MRB_ARGS_REQ(1)); /* override */
+ mrb_define_method(mrb, mrb->integer_class, "quo", rational_int_quo, MRB_ARGS_REQ(1)); /* override */
+#endif
+ mrb_define_method(mrb, mrb->kernel_module, "Rational", rational_m, MRB_ARGS_ARG(1,1));
+}
+
+void
+mrb_mruby_rational_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-rational/test/rational.rb b/mrbgems/mruby-rational/test/rational.rb
new file mode 100644
index 000000000..b807ef83f
--- /dev/null
+++ b/mrbgems/mruby-rational/test/rational.rb
@@ -0,0 +1,333 @@
+class UserDefinedNumeric < Numeric
+ def initialize(n)
+ @n = n
+ end
+
+ def <=>(rhs)
+ return nil unless rhs.respond_to?(:to_i)
+ rhs = rhs.to_i
+ rhs < 0 ? nil : @n <=> rhs
+ end
+
+ def inspect
+ "#{self.class}(#{@n})"
+ end
+end
+
+class ComplexLikeNumeric < UserDefinedNumeric
+ def ==(rhs)
+ @n == 0 && rhs == 0
+ end
+
+ undef <=>
+end
+
+def assert_rational(exp, real)
+ assert "assert_rational" do
+ assert_kind_of Rational, real
+ assert_float exp.numerator, real.numerator
+ assert_float exp.denominator, real.denominator
+ end
+end
+
+def assert_equal_rational(exp, o1, o2)
+ assert "assert_equal_rational" do
+ if exp
+ assert_operator(o1, :==, o2)
+ assert_not_operator(o1, :!=, o2)
+ else
+ assert_not_operator(o1, :==, o2)
+ assert_operator(o1, :!=, o2)
+ end
+ end
+end
+
+def assert_cmp(exp, o1, o2)
+ if exp == (o1 <=> o2)
+ pass
+ else
+ flunk "", " Expected #{o1.inspect} <=> #{o2.inspect} to be #{exp}."
+ end
+end
+
+def assert_complex(real, imag)
+ if Object.const_defined?(:Complex)
+ assert "assert_complex" do
+ c = yield
+ assert_float(real, c.real)
+ assert_float(imag, c.imaginary)
+ end
+ end
+end
+
+assert 'Rational' do
+ r = 5r
+ assert_equal(Rational, r.class)
+ assert_equal([5, 1], [r.numerator, r.denominator])
+end
+
+assert 'Kernel#Rational' do
+ r = Rational(4,10)
+ assert_equal(2, r.numerator)
+ assert_equal(5, r.denominator)
+
+ r = Rational(3)
+ assert_equal(3, r.numerator)
+ assert_equal(1, r.denominator)
+
+ assert_raise(ArgumentError) { Rational() }
+ assert_raise(ArgumentError) { Rational(1,2,3) }
+end
+
+assert 'Rational#to_f' do
+ assert_float(2.0, Rational(2).to_f)
+ assert_float(2.25, Rational(9, 4).to_f)
+ assert_float(-0.75, Rational(-3, 4).to_f)
+ assert_float(6.666666666666667, Rational(20, 3).to_f)
+end
+
+assert 'Rational#to_i' do
+ assert_equal(0, Rational(2, 3).to_i)
+ assert_equal(3, Rational(3).to_i)
+ assert_equal(300, Rational(300.6).to_i)
+ assert_equal(1, Rational(98, 71).to_i)
+ assert_equal(-15, Rational(-30, 2).to_i)
+end
+
+assert 'Rational#*' do
+ assert_rational(Rational(4, 9), Rational(2, 3) * Rational(2, 3))
+ assert_rational(Rational(900, 1), Rational(900) * Rational(1))
+ assert_rational(Rational(1, 1), Rational(-2, 9) * Rational(-9, 2))
+ assert_rational(Rational(9, 2), Rational(9, 8) * 4)
+ assert_float( 21.77777777777778, Rational(20, 9) * 9.8)
+ assert_float( 21.77777777777778, 9.8 * Rational(20, 9))
+ assert_complex(5.2, 2.6) {Rational(13,5)*(2.0+1i)}
+ assert_complex(5.2, 2.6) {(2.0+1i)*Rational(13,5)}
+end
+
+assert 'Rational#+' do
+ assert_rational(Rational(4, 3), Rational(2, 3) + Rational(2, 3))
+ assert_rational(Rational(901, 1), Rational(900) + Rational(1))
+ assert_rational(Rational(-85, 18), Rational(-2, 9) + Rational(-9, 2))
+ assert_rational(Rational(41, 8), Rational(9, 8) + 4)
+ assert_rational(Rational(41, 8), 4 + Rational(9, 8))
+ assert_float( 12.022222222222222, Rational(20, 9) + 9.8)
+ assert_float( 12.022222222222222, 9.8 + Rational(20, 9))
+ assert_complex(24.0, 0) {Rational(24,2)+(12.0+0i)}
+ assert_complex(24.0, 0) {(12.0+0i)+Rational(24,2)}
+end
+
+assert 'Rational#-' do
+ assert_rational(Rational(0, 1), Rational(2, 3) - Rational(2, 3))
+ assert_rational(Rational(899, 1), Rational(900) - Rational(1))
+ assert_rational(Rational(77, 18), Rational(-2, 9) - Rational(-9, 2))
+ assert_rational(Rational(23, 8), 4 - Rational(9, 8))
+ assert_float( -7.577777777777778, Rational(20, 9) - 9.8)
+ assert_float( 7.577777777777778, 9.8 - Rational(20, 9))
+ assert_complex(2.0, 0) {Rational(24,2)-(10.0+0i)}
+ assert_complex(2.0, 0) {(14.0+0i)-Rational(24,2)}
+end
+
+assert 'Rational#/' do
+ assert_rational(Rational(1, 1), Rational(2, 3) / Rational(2, 3))
+ assert_rational(Rational(900, 1), Rational(900) / Rational(1))
+ assert_rational(Rational(4, 81), Rational(-2, 9) / Rational(-9, 2))
+ assert_rational(Rational(9, 32), Rational(9, 8) / 4)
+ assert_rational(Rational(32, 9), 4 / Rational(9, 8))
+ assert_float( 0.22675736961451246, Rational(20, 9) / 9.8)
+ assert_float( 4.41, 9.8 / Rational(20, 9))
+ assert_complex(1.92, 1.44) {Rational(24,2)/(4.0-3i)}
+ assert_complex(0.25, 0.25) {(3.0+3i)/Rational(24,2)}
+end
+
+assert 'Rational#==, Rational#!=' do
+ assert_equal_rational(true, Rational(1,1), Rational(1))
+ assert_equal_rational(true, Rational(-1,1), -1r)
+ assert_equal_rational(true, Rational(13,4), 3.25)
+ assert_equal_rational(true, Rational(13,3.25), Rational(4,1))
+ assert_equal_rational(true, Rational(-3,-4), Rational(3,4))
+ assert_equal_rational(true, Rational(-4,5), Rational(4,-5))
+ assert_equal_rational(true, Rational(4,2), 2)
+ assert_equal_rational(true, Rational(-4,2), -2)
+ assert_equal_rational(true, Rational(4,-2), -2)
+ assert_equal_rational(true, Rational(4,2), 2.0)
+ assert_equal_rational(true, Rational(-4,2), -2.0)
+ assert_equal_rational(true, Rational(4,-2), -2.0)
+ assert_equal_rational(true, Rational(8,6), Rational(4,3))
+ assert_equal_rational(false, Rational(13,4), 3)
+ assert_equal_rational(false, Rational(13,4), 3.3)
+ assert_equal_rational(false, Rational(2,1), 1r)
+ assert_equal_rational(false, Rational(1), nil)
+ assert_equal_rational(false, Rational(1), '')
+ assert_equal_rational(true, 0r, UserDefinedNumeric.new(0))
+ assert_equal_rational(true, 1r, UserDefinedNumeric.new(1))
+ assert_equal_rational(false, 1r, UserDefinedNumeric.new(2))
+ assert_equal_rational(false, -1r, UserDefinedNumeric.new(-1))
+ assert_equal_rational(true, 0r, ComplexLikeNumeric.new(0))
+ assert_equal_rational(false, 1r, ComplexLikeNumeric.new(1))
+ assert_equal_rational(false, 1r, ComplexLikeNumeric.new(2))
+end
+
+assert 'Integer#==(Rational), Integer#!=(Rational)' do
+ assert_equal_rational(true, 2, Rational(4,2))
+ assert_equal_rational(true, -2, Rational(-4,2))
+ assert_equal_rational(true, -2, Rational(4,-2))
+ assert_equal_rational(false, 3, Rational(13,4))
+end
+
+assert 'Float#==(Rational), Float#!=(Rational)' do
+ assert_equal_rational(true, 2.0, Rational(4,2))
+ assert_equal_rational(true, -2.0, Rational(-4,2))
+ assert_equal_rational(true, -2.0, Rational(4,-2))
+ assert_equal_rational(false, 3.3, Rational(13,4))
+end
+
+assert 'Rational#<=>' do
+ assert_cmp(-1, Rational(-1), Rational(0))
+ assert_cmp(0, Rational(0), Rational(0))
+ assert_cmp(1, Rational(1), Rational(0))
+ assert_cmp(-1, Rational(-1), 0)
+ assert_cmp(0, Rational(0), 0)
+ assert_cmp(1, Rational(1), 0)
+ assert_cmp(-1, Rational(-1), 0.0)
+ assert_cmp(0, Rational(0), 0.0)
+ assert_cmp(1, Rational(1), 0.0)
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(0, Rational(2,3), Rational(2,3))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(1, Rational(0), Rational(-1))
+ assert_cmp(-1, Rational(0), Rational(1))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(0, Rational(2,3), Rational(2,3))
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(nil, 3r, "3")
+ assert_cmp(1, 3r, UserDefinedNumeric.new(2))
+ assert_cmp(0, 3r, UserDefinedNumeric.new(3))
+ assert_cmp(-1, 3r, UserDefinedNumeric.new(4))
+ assert_cmp(nil, Rational(-3), UserDefinedNumeric.new(5))
+ assert_raise(NoMethodError) { 0r <=> ComplexLikeNumeric.new(0) }
+ assert_raise(NoMethodError) { 1r <=> ComplexLikeNumeric.new(2) }
+end
+
+assert 'Integer#<=>(Rational)' do
+ assert_cmp(-1, -2, Rational(-9,5))
+ assert_cmp(0, 5, 5r)
+ assert_cmp(1, 3, Rational(8,3))
+end
+
+assert 'Float#<=>(Rational)' do
+ assert_cmp(-1, -2.1, Rational(-9,5))
+ assert_cmp(0, 5.0, 5r)
+ assert_cmp(1, 2.7, Rational(8,3))
+end
+
+assert 'Rational#<' do
+ assert_operator(Rational(1,2), :<, Rational(2,3))
+ assert_not_operator(Rational(2,3), :<, Rational(2,3))
+ assert_operator(Rational(2,3), :<, 1)
+ assert_not_operator(2r, :<, 2)
+ assert_not_operator(Rational(2,3), :<, -3)
+ assert_operator(Rational(-4,3), :<, -0.3)
+ assert_not_operator(Rational(13,4), :<, 3.25)
+ assert_not_operator(Rational(2,3), :<, 0.6)
+ assert_raise(ArgumentError) { 1r < "2" }
+end
+
+assert 'Integer#<(Rational)' do
+ assert_not_operator(1, :<, Rational(2,3))
+ assert_not_operator(2, :<, 2r)
+ assert_operator(-3, :<, Rational(2,3))
+end
+
+assert 'Float#<(Rational)' do
+ assert_not_operator(-0.3, :<, Rational(-4,3))
+ assert_not_operator(3.25, :<, Rational(13,4))
+ assert_operator(0.6, :<, Rational(2,3))
+end
+
+assert 'Rational#<=' do
+ assert_operator(Rational(1,2), :<=, Rational(2,3))
+ assert_operator(Rational(2,3), :<=, Rational(2,3))
+ assert_operator(Rational(2,3), :<=, 1)
+ assert_operator(2r, :<=, 2)
+ assert_not_operator(Rational(2,3), :<=, -3)
+ assert_operator(Rational(-4,3), :<=, -0.3)
+ assert_operator(Rational(13,4), :<=, 3.25)
+ assert_not_operator(Rational(2,3), :<=, 0.6)
+ assert_raise(ArgumentError) { 1r <= "2" }
+end
+
+assert 'Integer#<=(Rational)' do
+ assert_not_operator(1, :<=, Rational(2,3))
+ assert_operator(2, :<=, 2r)
+ assert_operator(-3, :<=, Rational(2,3))
+end
+
+assert 'Float#<=(Rational)' do
+ assert_not_operator(-0.3, :<=, Rational(-4,3))
+ assert_operator(3.25, :<=, Rational(13,4))
+ assert_operator(0.6, :<=, Rational(2,3))
+end
+
+assert 'Rational#>' do
+ assert_not_operator(Rational(1,2), :>, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>, 1)
+ assert_not_operator(2r, :>, 2)
+ assert_operator(Rational(2,3), :>, -3)
+ assert_not_operator(Rational(-4,3), :>, -0.3)
+ assert_not_operator(Rational(13,4), :>, 3.25)
+ assert_operator(Rational(2,3), :>, 0.6)
+ assert_raise(ArgumentError) { 1r > "2" }
+end
+
+assert 'Integer#>(Rational)' do
+ assert_operator(1, :>, Rational(2,3))
+ assert_not_operator(2, :>, 2r)
+ assert_not_operator(-3, :>, Rational(2,3))
+end
+
+assert 'Float#>(Rational)' do
+ assert_operator(-0.3, :>, Rational(-4,3))
+ assert_not_operator(3.25, :>, Rational(13,4))
+ assert_not_operator(0.6, :>, Rational(2,3))
+end
+
+assert 'Rational#>=' do
+ assert_not_operator(Rational(1,2), :>=, Rational(2,3))
+ assert_operator(Rational(2,3), :>=, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>=, 1)
+ assert_operator(2r, :>=, 2)
+ assert_operator(Rational(2,3), :>=, -3)
+ assert_not_operator(Rational(-4,3), :>=, -0.3)
+ assert_operator(Rational(13,4), :>=, 3.25)
+ assert_operator(Rational(2,3), :>=, 0.6)
+ assert_raise(ArgumentError) { 1r >= "2" }
+end
+
+assert 'Integer#>=(Rational)' do
+ assert_operator(1, :>=, Rational(2,3))
+ assert_operator(2, :>=, 2r)
+ assert_not_operator(-3, :>=, Rational(2,3))
+end
+
+assert 'Float#>=(Rational)' do
+ assert_operator(-0.3, :>=, Rational(-4,3))
+ assert_operator(3.25, :>=, Rational(13,4))
+ assert_not_operator(0.6, :>=, Rational(2,3))
+end
+
+assert 'Rational#negative?' do
+ assert_predicate(Rational(-2,3), :negative?)
+ assert_predicate(Rational(2,-3), :negative?)
+ assert_not_predicate(Rational(2,3), :negative?)
+ assert_not_predicate(Rational(0), :negative?)
+end
+
+assert 'Rational#frozen?' do
+ assert_predicate(1r, :frozen?)
+ assert_predicate(Rational(2,3), :frozen?)
+ assert_predicate(4/5r, :frozen?)
+end
diff --git a/mrbgems/mruby-sleep/.gitignore b/mrbgems/mruby-sleep/.gitignore
deleted file mode 100644
index b9f9e71df..000000000
--- a/mrbgems/mruby-sleep/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/mruby
-
-*.so
-*.a \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis.yml b/mrbgems/mruby-sleep/.travis.yml
deleted file mode 100644
index ad6b007b6..000000000
--- a/mrbgems/mruby-sleep/.travis.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-dist: 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 test \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis_build_config.rb b/mrbgems/mruby-sleep/.travis_build_config.rb
deleted file mode 100644
index b32e38a9d..000000000
--- a/mrbgems/mruby-sleep/.travis_build_config.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-MRuby::Build.new do |conf|
- toolchain :gcc
- conf.gembox 'default'
- conf.gem '../mruby-sleep'
- conf.enable_test
-end
diff --git a/mrbgems/mruby-sleep/README.md b/mrbgems/mruby-sleep/README.md
index 4543c49cf..ed9c2730a 100644
--- a/mrbgems/mruby-sleep/README.md
+++ b/mrbgems/mruby-sleep/README.md
@@ -1,27 +1,29 @@
# Sleep Module for mruby
+
mruby sleep module
-## install by mrbgems
- - add conf.gem line to `build_config.rb`
+## Install by mrbgems
+
+* add `conf.gem` line to your build configuration.
+
```ruby
MRuby::Build.new do |conf|
# ... (snip) ...
- conf.gem :git => 'https://github.com/matsumoto-r/mruby-sleep.git'
+ conf.gem :core => 'mruby-sleep'
end
```
-## example
+## Example
```ruby
-Sleep::sleep(10)
-Sleep::usleep(10000)
+sleep(10)
+usleep(10000)
```
# License
-under the MIT License:
-
-* http://www.opensource.org/licenses/mit-license.php
+under the MIT License:
+* <https://www.opensource.org/licenses/mit-license.php>
diff --git a/mrbgems/mruby-sleep/Rakefile b/mrbgems/mruby-sleep/Rakefile
deleted file mode 100644
index 5e3c46173..000000000
--- a/mrbgems/mruby-sleep/Rakefile
+++ /dev/null
@@ -1,29 +0,0 @@
-MRUBY_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
diff --git a/mrbgems/mruby-sleep/example/sleep.rb b/mrbgems/mruby-sleep/example/sleep.rb
index dfe86af27..90a436937 100644
--- a/mrbgems/mruby-sleep/example/sleep.rb
+++ b/mrbgems/mruby-sleep/example/sleep.rb
@@ -1,3 +1,2 @@
-Sleep::sleep(10)
-Sleep::usleep(10000)
-
+sleep(10)
+usleep(10000)
diff --git a/mrbgems/mruby-sleep/mrbgem.rake b/mrbgems/mruby-sleep/mrbgem.rake
index 8827b3580..024106a48 100644
--- a/mrbgems/mruby-sleep/mrbgem.rake
+++ b/mrbgems/mruby-sleep/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-sleep') do |spec|
spec.license = 'MIT'
- spec.authors = 'MATSUMOTO Ryosuke'
- spec.version = '0.0.1'
+ spec.authors = ['MATSUMOTO Ryosuke', 'mruby developers']
+ spec.summary = 'Kernel#sleep and Kernel#usleep'
end
diff --git a/mrbgems/mruby-sleep/src/mrb_sleep.c b/mrbgems/mruby-sleep/src/sleep.c
index ce643cf12..79a5af650 100644
--- a/mrbgems/mruby-sleep/src/mrb_sleep.c
+++ b/mrbgems/mruby-sleep/src/sleep.c
@@ -2,6 +2,7 @@
** mrb_sleep - sleep methods for mruby
**
** Copyright (c) mod_mruby developers 2012-
+** Copyright (c) mruby developers 2018
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
@@ -22,14 +23,14 @@
** 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 ]
+** [ MIT license: https://www.opensource.org/licenses/mit-license.php ]
*/
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x * 1000)
- #define usleep(x) Sleep(((x)<1000) ? 1 : ((x)/1000))
+ #define usleep(x) Sleep((DWORD)(((x)<1000) ? 1 : ((x)/1000)))
#else
#include <unistd.h>
#include <sys/time.h>
@@ -43,7 +44,7 @@ mrb_f_sleep(mrb_state *mrb, mrb_value self)
{
time_t beg = time(0);
time_t end;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
mrb_float sec;
mrb_get_args(mrb, "f", &sec);
@@ -51,7 +52,7 @@ mrb_f_sleep(mrb_state *mrb, mrb_value self)
usleep(sec * 1000000);
}
else {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer");
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
}
#else
mrb_int sec;
@@ -60,12 +61,12 @@ mrb_f_sleep(mrb_state *mrb, mrb_value self)
if (sec >= 0) {
sleep(sec);
} else {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer");
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
}
#endif
end = time(0) - beg;
- return mrb_fixnum_value(end);
+ return mrb_fixnum_value((mrb_int)end);
}
/* mruby special; needed for mruby without float numbers */
@@ -94,7 +95,7 @@ mrb_f_usleep(mrb_state *mrb, mrb_value self)
if (usec >= 0) {
usleep(usec);
} else {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer");
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative integer");
}
#ifdef _WIN32
@@ -119,7 +120,7 @@ mrb_f_usleep(mrb_state *mrb, mrb_value self)
}
#endif
- return mrb_fixnum_value(slp_tm);
+ return mrb_fixnum_value((mrb_int)slp_tm);
}
void
diff --git a/mrbgems/mruby-sleep/test/sleep_test.rb b/mrbgems/mruby-sleep/test/sleep_test.rb
index 74030c0d3..f05b7a30b 100644
--- a/mrbgems/mruby-sleep/test/sleep_test.rb
+++ b/mrbgems/mruby-sleep/test/sleep_test.rb
@@ -1,36 +1,29 @@
-def 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_nothing_raised { sleep(1) }
+ assert_nothing_raised { sleep(0) }
+end
- assert_nil e
+assert("sleep would accept non-negative float value") do
+ skip unless Object.const_defined?(:Float)
+ assert_nothing_raised { sleep(0.01) }
+ assert_nothing_raised { sleep(0.0) }
+ assert_nothing_raised { sleep(-0.0) }
end
-assert("sleep would not accept negative value") do
- e = run_with_catching_error{ sleep -1 }
+assert("sleep would not accept negative integer value") do
+ assert_raise(ArgumentError) { sleep(-1) }
+end
- assert_not_equal e, nil
- assert_equal e.class, ArgumentError
+assert("sleep would not accept negative float value") do
+ skip unless Object.const_defined?(:Float)
+ assert_raise(ArgumentError) { sleep(-0.1) }
end
assert("usleep works") do
- e = run_with_catching_error { usleep 100 }
-
- assert_nil e
+ assert_nothing_raised { usleep(100) }
+ assert_nothing_raised { usleep(0) }
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
+ assert_raise(ArgumentError) { usleep(-100) }
end
diff --git a/mrbgems/mruby-socket/.travis.yml b/mrbgems/mruby-socket/.travis.yml
deleted file mode 100644
index 6476289bd..000000000
--- a/mrbgems/mruby-socket/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-language: c
-sudo: false
-script:
- - "ruby run_test.rb all test"
diff --git a/mrbgems/mruby-socket/README.md b/mrbgems/mruby-socket/README.md
index 7428cfa6c..8084cdb70 100644
--- a/mrbgems/mruby-socket/README.md
+++ b/mrbgems/mruby-socket/README.md
@@ -1,11 +1,10 @@
-mruby-socket
-============
+# mruby-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)
@@ -20,21 +19,23 @@ 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...
+
+* [mruby-io](https://github.com/mruby/mruby/tree/master/mrbgems/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`
+* 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.
+Copyright (c) 2017 mruby developers
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/mrbgems/mruby-socket/mrbgem.rake b/mrbgems/mruby-socket/mrbgem.rake
index b0894e095..e58761356 100644
--- a/mrbgems/mruby-socket/mrbgem.rake
+++ b/mrbgems/mruby-socket/mrbgem.rake
@@ -1,13 +1,13 @@
MRuby::Gem::Specification.new('mruby-socket') do |spec|
spec.license = 'MIT'
- spec.authors = 'Internet Initiative Japan'
+ spec.authors = ['Internet Initiative Japan', 'mruby developers']
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
+ if spec.for_windows?
spec.linker.libraries << "wsock32"
spec.linker.libraries << "ws2_32"
end
diff --git a/mrbgems/mruby-socket/run_test.rb b/mrbgems/mruby-socket/run_test.rb
deleted file mode 100644
index 87a222f16..000000000
--- a/mrbgems/mruby-socket/run_test.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/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
diff --git a/mrbgems/mruby-socket/src/socket.c b/mrbgems/mruby-socket/src/socket.c
index b44371544..c7de66d41 100644
--- a/mrbgems/mruby-socket/src/socket.c
+++ b/mrbgems/mruby-socket/src/socket.c
@@ -10,6 +10,7 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
+ #include <winerror.h>
#define SHUT_RDWR SD_BOTH
#ifndef _SSIZE_T_DEFINED
@@ -37,9 +38,11 @@
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/data.h"
+#include "mruby/numeric.h"
#include "mruby/string.h"
#include "mruby/variable.h"
-#include "error.h"
+#include "mruby/error.h"
+#include "mruby/presym.h"
#include "mruby/ext/io.h"
@@ -51,7 +54,7 @@
#endif
#endif
-#define E_SOCKET_ERROR (mrb_class_get(mrb, "SocketError"))
+#define E_SOCKET_ERROR mrb_class_get_id(mrb, MRB_SYM(SocketError))
#if !defined(mrb_cptr)
#define mrb_cptr_value(m,p) mrb_voidp_value((m),(p))
@@ -62,52 +65,52 @@
#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;
+ 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;
+ struct addrinfo hints, *res, *ressave;
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = af;
+ 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;
- }
+ if (getaddrinfo(src, NULL, &hints, &res) != 0)
+ {
+ printf("Couldn't resolve host %s\n", src);
+ return -1;
+ }
- ressave = res;
+ ressave = res;
- while (res)
- {
- memcpy(dst, res->ai_addr, res->ai_addrlen);
- res = res->ai_next;
- }
+ while (res)
+ {
+ memcpy(dst, res->ai_addr, res->ai_addrlen);
+ res = res->ai_next;
+ }
- freeaddrinfo(ressave);
- return 0;
+ freeaddrinfo(ressave);
+ return 0;
}
#endif
@@ -129,7 +132,7 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "oo|oooi", &nodename, &service, &family, &socktype, &protocol, &flags);
if (mrb_string_p(nodename)) {
- hostname = mrb_str_to_cstr(mrb, nodename);
+ hostname = RSTRING_CSTR(mrb, nodename);
} else if (mrb_nil_p(nodename)) {
hostname = NULL;
} else {
@@ -137,51 +140,51 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
}
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));
+ servname = RSTRING_CSTR(mrb, service);
+ } else if (mrb_integer_p(service)) {
+ servname = RSTRING_PTR(mrb_integer_to_str(mrb, service, 10));
} else if (mrb_nil_p(service)) {
servname = NULL;
} else {
- mrb_raise(mrb, E_TYPE_ERROR, "service must be String, Fixnum, or nil");
+ mrb_raise(mrb, E_TYPE_ERROR, "service must be String, Integer, 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_integer_p(family)) {
+ hints.ai_family = (int)mrb_integer(family);
}
- if (mrb_fixnum_p(socktype)) {
- hints.ai_socktype = (int)mrb_fixnum(socktype);
+ if (mrb_integer_p(socktype)) {
+ hints.ai_socktype = (int)mrb_integer(socktype);
}
- if (mrb_fixnum_p(protocol)) {
- hints.ai_protocol = (int)mrb_fixnum(protocol);
+ if (mrb_integer_p(protocol)) {
+ hints.ai_protocol = (int)mrb_integer(protocol);
}
- lastai = mrb_cv_get(mrb, klass, mrb_intern_lit(mrb, "_lastai"));
+ lastai = mrb_cv_get(mrb, klass, MRB_SYM(_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());
+ mrb_cv_set(mrb, klass, MRB_SYM(_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_raisef(mrb, E_SOCKET_ERROR, "getaddrinfo: %s", gai_strerror(error));
}
- mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_cptr_value(mrb, res0));
+ mrb_cv_set(mrb, klass, MRB_SYM(_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));
+ ai = mrb_funcall_id(mrb, klass, MRB_SYM(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());
+ mrb_cv_set(mrb, klass, MRB_SYM(_lastai), mrb_nil_value());
return ary;
}
@@ -195,16 +198,16 @@ mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self)
flags = 0;
mrb_get_args(mrb, "|i", &flags);
- host = mrb_str_buf_new(mrb, NI_MAXHOST);
- serv = mrb_str_buf_new(mrb, NI_MAXSERV);
+ host = mrb_str_new_capa(mrb, NI_MAXHOST);
+ serv = mrb_str_new_capa(mrb, NI_MAXSERV);
- sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
+ sastr = mrb_iv_get(mrb, self, MRB_IVSYM(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)));
+ mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error));
}
ary = mrb_ary_new_capa(mrb, 2);
mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
@@ -220,7 +223,7 @@ mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self)
{
mrb_value sastr;
- sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
+ sastr = mrb_iv_get(mrb, self, MRB_IVSYM(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) {
@@ -252,7 +255,7 @@ sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen)
return mrb_nil_value();
}
port = ntohs(port);
- host = mrb_str_buf_new(mrb, NI_MAXHOST);
+ host = mrb_str_new_capa(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)));
@@ -267,7 +270,7 @@ sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen)
static int
socket_fd(mrb_state *mrb, mrb_value sock)
{
- return (int)mrb_fixnum(mrb_funcall(mrb, sock, "fileno", 0));
+ return (int)mrb_integer(mrb_funcall_id(mrb, sock, MRB_SYM(fileno), 0));
}
static int
@@ -345,10 +348,10 @@ mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value 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"));
+ c = mrb_const_get(mrb, mrb_obj_value(mrb_class_get_id(mrb, MRB_SYM(Socket))), MRB_SYM(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);
+ return mrb_funcall_id(mrb, c, MRB_SYM(new), 4, mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data);
}
static mrb_value
@@ -359,7 +362,7 @@ mrb_basicsocket_recv(mrb_state *mrb, mrb_value self)
mrb_value buf;
mrb_get_args(mrb, "i|i", &maxlen, &flags);
- buf = mrb_str_buf_new(mrb, maxlen);
+ buf = mrb_str_new_capa(mrb, maxlen);
n = recv(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags);
if (n == -1)
mrb_sys_fail(mrb, "recv");
@@ -376,9 +379,9 @@ mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self)
socklen_t socklen;
mrb_get_args(mrb, "i|i", &maxlen, &flags);
- buf = mrb_str_buf_new(mrb, maxlen);
+ buf = mrb_str_new_capa(mrb, maxlen);
socklen = sizeof(struct sockaddr_storage);
- sa = mrb_str_buf_new(mrb, socklen);
+ sa = mrb_str_new_capa(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");
@@ -447,21 +450,21 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
argc = mrb_get_args(mrb, "o|io", &so, &optname, &optval);
if (argc == 3) {
- if (!mrb_fixnum_p(so)) {
+ if (!mrb_integer_p(so)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "level is not an integer");
}
- level = mrb_fixnum(so);
+ level = mrb_integer(so);
if (mrb_string_p(optval)) {
/* that's good */
- } else if (mrb_type(optval) == MRB_TT_TRUE || mrb_type(optval) == MRB_TT_FALSE) {
+ } else if (mrb_true_p(optval) || mrb_false_p(optval)) {
mrb_int i = mrb_test(optval) ? 1 : 0;
optval = mrb_str_new(mrb, (char*)&i, sizeof(i));
- } else if (mrb_fixnum_p(optval)) {
+ } else if (mrb_integer_p(optval)) {
if (optname == IP_MULTICAST_TTL || optname == IP_MULTICAST_LOOP) {
- char uc = (char)mrb_fixnum(optval);
+ char uc = (char)mrb_integer(optval);
optval = mrb_str_new(mrb, &uc, sizeof(uc));
} else {
- mrb_int i = mrb_fixnum(optval);
+ mrb_int i = mrb_integer(optval);
optval = mrb_str_new(mrb, (char*)&i, sizeof(i));
}
} else {
@@ -470,11 +473,11 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
} 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);
+ level = mrb_integer(mrb_funcall_id(mrb, so, MRB_SYM(level), 0));
+ optname = mrb_integer(mrb_funcall_id(mrb, so, MRB_SYM(optname), 0));
+ optval = mrb_funcall_id(mrb, so, MRB_SYM(data), 0);
} else {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 3)", mrb_fixnum_value(argc));
+ mrb_argnum_error(mrb, argc, 3, 3);
}
s = socket_fd(mrb, self);
@@ -513,7 +516,8 @@ static mrb_value
mrb_ipsocket_ntop(mrb_state *mrb, mrb_value klass)
{
mrb_int af, n;
- char *addr, buf[50];
+ const char *addr;
+ char buf[50];
mrb_get_args(mrb, "is", &af, &addr, &n);
if ((af == AF_INET && n != 4) || (af == AF_INET6 && n != 16))
@@ -527,7 +531,8 @@ static mrb_value
mrb_ipsocket_pton(mrb_state *mrb, mrb_value klass)
{
mrb_int af, n;
- char *bp, buf[50];
+ const char *bp;
+ char buf[50];
mrb_get_args(mrb, "is", &af, &bp, &n);
if ((size_t)n > sizeof(buf) - 1)
@@ -566,10 +571,10 @@ mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self)
fd = socket_fd(mrb, self);
flags = 0;
mrb_get_args(mrb, "i|i", &maxlen, &flags);
- buf = mrb_str_buf_new(mrb, maxlen);
+ buf = mrb_str_new_capa(mrb, maxlen);
socklen = sizeof(ss);
n = recvfrom(fd, RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags,
- (struct sockaddr *)&ss, &socklen);
+ (struct sockaddr *)&ss, &socklen);
if (n == -1) {
mrb_sys_fail(mrb, "recvfrom");
}
@@ -592,7 +597,7 @@ mrb_socket_gethostname(mrb_state *mrb, mrb_value cls)
#else
bufsize = 256;
#endif
- buf = mrb_str_buf_new(mrb, (mrb_int)bufsize);
+ buf = mrb_str_new_capa(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)));
@@ -623,7 +628,7 @@ mrb_socket_accept2(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "i", &s0);
socklen = sizeof(struct sockaddr_storage);
- sastr = mrb_str_buf_new(mrb, socklen);
+ sastr = mrb_str_new_capa(mrb, socklen);
s1 = (int)accept(s0, (struct sockaddr *)RSTRING_PTR(sastr), &socklen);
if (s1 == -1) {
mrb_sys_fail(mrb, "accept");
@@ -700,9 +705,9 @@ mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass)
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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %d bytes)", (int)sizeof(sunp->sun_path) - 1);
}
- s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un));
+ s = mrb_str_new_capa(mrb, sizeof(struct sockaddr_un));
sunp = (struct sockaddr_un *)RSTRING_PTR(s);
#if HAVE_SA_LEN
sunp->sun_len = sizeof(struct sockaddr_un);
@@ -776,7 +781,6 @@ mrb_win32_basicsocket_close(mrb_state *mrb, mrb_value self)
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)
{
@@ -839,7 +843,7 @@ mrb_win32_basicsocket_syswrite(mrb_state *mrb, mrb_value self)
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);
+ return mrb_int_value(mrb, n);
}
#endif
@@ -859,14 +863,14 @@ mrb_mruby_socket_gem_init(mrb_state* mrb)
#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_mod_cv_set(mrb, ai, MRB_SYM(_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");
+ io = mrb_class_get_id(mrb, MRB_SYM(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));
@@ -934,8 +938,8 @@ mrb_mruby_socket_gem_init(mrb_state* mrb)
constants = mrb_define_module_under(mrb, sock, "Constants");
#define define_const(SYM) \
- do { \
- mrb_define_const(mrb, constants, #SYM, mrb_fixnum_value(SYM)); \
+ do { \
+ mrb_define_const(mrb, constants, #SYM, mrb_int_value(mrb, SYM)); \
} while (0)
#include "const.cstub"
@@ -945,7 +949,7 @@ 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"));
+ ai = mrb_mod_cv_get(mrb, mrb_class_get_id(mrb, MRB_SYM(Addrinfo)), MRB_SYM(_lastai));
if (mrb_cptr_p(ai)) {
freeaddrinfo((struct addrinfo*)mrb_cptr(ai));
}
diff --git a/mrbgems/mruby-socket/test/socket.rb b/mrbgems/mruby-socket/test/socket.rb
index efd8fc28e..b64a67919 100644
--- a/mrbgems/mruby-socket/test/socket.rb
+++ b/mrbgems/mruby-socket/test/socket.rb
@@ -28,7 +28,7 @@ assert('Socket#recvfrom') do
rstr, ai = s.recvfrom sstr.size
assert_equal sstr, rstr
- assert_true "127.0.0.1", ai.ip_address
+ assert_equal "127.0.0.1", ai.ip_address
ensure
s.close rescue nil
c.close rescue nil
diff --git a/mrbgems/mruby-socket/test/sockettest.c b/mrbgems/mruby-socket/test/sockettest.c
index 3017c7cc1..02eb1e1a8 100644
--- a/mrbgems/mruby-socket/test/sockettest.c
+++ b/mrbgems/mruby-socket/test/sockettest.c
@@ -1,4 +1,3 @@
-#include <stdio.h>
#include <stdlib.h>
#include "mruby.h"
@@ -8,7 +7,9 @@
#include <io.h>
-#ifdef _MSC_VER
+#if defined(_MSC_VER) || \
+ (defined(MRB_MINGW32_VERSION) && MRB_MINGW32_VERSION < 3021) || \
+ (defined(MRB_MINGW64_VERSION) && MRB_MINGW64_VERSION < 4000)
#include <fcntl.h>
#include <sys/stat.h>
diff --git a/mrbgems/mruby-sprintf/src/kernel.c b/mrbgems/mruby-sprintf/src/kernel.c
deleted file mode 100644
index 946b43a8a..000000000
--- a/mrbgems/mruby-sprintf/src/kernel.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-** kernel.c - Kernel module suppliment
-**
-** See Copyright Notice in mruby.h
-*/
-
-#include <mruby.h>
-
-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. */
-}
-
diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c
index 15d7b5464..ea9d74d95 100644
--- a/mrbgems/mruby-sprintf/src/sprintf.c
+++ b/mrbgems/mruby-sprintf/src/sprintf.c
@@ -5,14 +5,13 @@
*/
#include <mruby.h>
-
#include <limits.h>
-#include <stdio.h>
#include <string.h>
#include <mruby/string.h>
#include <mruby/hash.h>
#include <mruby/numeric.h>
-#ifndef MRB_WITHOUT_FLOAT
+#include <mruby/presym.h>
+#ifndef MRB_NO_FLOAT
#include <math.h>
#endif
#include <ctype.h>
@@ -22,9 +21,6 @@
#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)
@@ -72,40 +68,38 @@ sign_bits(int base, const char *p)
return c;
}
-static mrb_value
-mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
+static char *
+mrb_uint_to_cstr(char *buf, size_t len, mrb_int num, int base)
{
- char buf[66], *b = buf + sizeof buf;
- mrb_int num = mrb_fixnum(x);
- uint64_t val = (uint64_t)num;
+ char *b = buf + len - 1;
+ const int mask = base-1;
+ int shift;
+ mrb_uint val = (uint64_t)num;
char d;
- if (base != 2) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
+ if (num == 0) {
+ buf[0] = '0'; buf[1] = '\0';
+ return buf;
}
- if (val == 0) {
- return mrb_str_new_lit(mrb, "0");
+ switch (base) {
+ case 16: d = 'f'; shift = 4; break;
+ case 8: d = '7'; shift = 3; break;
+ case 2: d = '1'; shift = 1; break;
+ default: return NULL;
}
*--b = '\0';
do {
- *--b = mrb_digitmap[(int)(val % base)];
- } while (val /= base);
+ *--b = mrb_digitmap[(int)(val & mask)];
+ } while (val >>= shift);
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);
+ return b;
}
#define FNONE 0
@@ -118,7 +112,52 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
#define FPREC 64
#define FPREC0 128
-#define CHECK(l) do {\
+#ifndef MRB_NO_FLOAT
+static int
+fmt_float(char *buf, size_t buf_size, char fmt, int flags, mrb_int width, int prec, mrb_float f)
+{
+ char sign = '\0';
+ int left_align = 0;
+ int zero_pad = 0;
+
+ if (flags & FSHARP) fmt |= 0x80;
+ if (flags & FPLUS) sign = '+';
+ if (flags & FMINUS) left_align = 1;
+ if (flags & FZERO) zero_pad = 1;
+ if (flags & FSPACE) sign = ' ';
+
+ int len = mrb_format_float(f, buf, buf_size, fmt, prec, sign);
+
+ // buf[0] < '0' returns true if the first character is space, + or -
+ // buf[1] < '9' matches a digit, and doesn't match when we get back +nan or +inf
+ if (buf[0] < '0' && buf[1] <= '9' && zero_pad) {
+ buf++;
+ width--;
+ len--;
+ }
+ if (*buf < '0' || *buf >= '9') {
+ // For inf or nan, we don't want to zero pad.
+ zero_pad = 0;
+ }
+ if (len >= width) {
+ return len;
+ }
+ buf[width] = '\0';
+ if (left_align) {
+ memset(&buf[len], ' ', width - len);
+ return width;
+ }
+ memmove(&buf[width - len], buf, len);
+ if (zero_pad) {
+ memset(buf, '0', width - len);
+ } else {
+ memset(buf, ' ', width - len);
+ }
+ return width;
+}
+#endif
+
+#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;\
@@ -144,10 +183,10 @@ 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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%d) mixed with numbered", nextarg);
break;
case -2:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%d) mixed with named", nextarg);
break;
default:
break;
@@ -155,29 +194,29 @@ check_next_arg(mrb_state *mrb, int posarg, int nextarg)
}
static void
-check_pos_arg(mrb_state *mrb, mrb_int posarg, mrb_int n)
+check_pos_arg(mrb_state *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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%i) after unnumbered(%d)",
+ n, posarg);
}
if (posarg == -2) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%i) after named", n);
}
if (n < 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %i$", n);
}
}
static void
-check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
+check_name_arg(mrb_state *mrb, int posarg, const char *name, size_t 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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%l after unnumbered(%d)",
+ name, len, posarg);
}
if (posarg == -1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len)));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%l after numbered", name, len);
}
}
@@ -194,25 +233,18 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
#define GETNTHARG(nth) \
((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth])
-#define GETNAMEARG(id, name, len) (\
+#define CHECKNAMEARG(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]"); \
- }
+ posarg = -2)
+
+#define GETNUM(n, val) do { \
+ if (!(p = get_num(mrb, p, end, &(n)))) \
+ mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big 1"); \
+} while(0)
#define GETASTER(num) do { \
mrb_value tmp_v; \
t = p++; \
- n = 0; \
GETNUM(n, val); \
if (*p == '$') { \
tmp_v = GETPOSARG(n); \
@@ -221,23 +253,33 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
tmp_v = GETNEXTARG(); \
p = t; \
} \
- num = mrb_int(mrb, tmp_v); \
+ num = mrb_as_int(mrb, tmp_v); \
} while (0)
-static mrb_value
+static const char *
+get_num(mrb_state *mrb, const char *p, const char *end, int *valp)
+{
+ char *e;
+ mrb_int n = mrb_int_read(p, end, &e);
+ if (e == NULL || n > INT_MAX) return NULL;
+ *valp = (int)n;
+ return e;
+}
+
+static void
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 (!mrb_undef_p(*hash)) return;
if (argc != 2) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
}
- tmp = mrb_check_convert_type(mrb, argv[1], MRB_TT_HASH, "Hash", "to_hash");
+ 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);
+ *hash = tmp;
}
/*
@@ -283,24 +325,20 @@ get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv)
*
* Field | Float Format
* ------+--------------------------------------------------------------
- * e | Convert floating point argument into exponential notation
+ * 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,
+ * 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
+ * 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
* ------+--------------------------------------------------------------
@@ -335,7 +373,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv)
* | | For the conversions 'x', 'X', 'b' and 'B'
* | | on non-zero, prefix the result with "0x",
* | | "0X", "0b" and "0B", respectively.
- * | | For 'a', 'A', 'e', 'E', 'f', 'g', and 'G',
+ * | | For '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.
@@ -494,18 +532,18 @@ get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv)
* For more complex formatting, Ruby supports a reference by name.
* %<name>s style uses format style, but %{name} style doesn't.
*
- * Exapmles:
+ * Examples:
* sprintf("%<foo>d : %<bar>f", { :foo => 1, :bar => 2 })
* #=> 1 : 2.000000
* sprintf("%{foo}f", { :foo => 1 })
* # => "1f"
*/
-mrb_value
+static mrb_value
mrb_f_sprintf(mrb_state *mrb, mrb_value obj)
{
mrb_int argc;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_get_args(mrb, "*", &argv, &argc);
@@ -526,33 +564,33 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
mrb_int blen;
mrb_int bsiz;
mrb_value result;
- mrb_int n;
- mrb_int width;
- mrb_int prec;
+ int n;
+ int width;
+ 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_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"); \
+#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;
- fmt = mrb_str_to_str(mrb, fmt);
+ mrb_to_str(mrb, fmt);
p = RSTRING_PTR(fmt);
end = p + RSTRING_LEN(fmt);
blen = 0;
@@ -580,7 +618,7 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
retry:
switch (*p) {
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p);
break;
case ' ':
@@ -615,11 +653,10 @@ 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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %i$", n);
}
nextvalue = GETPOSARG(n);
p++;
@@ -634,19 +671,21 @@ retry:
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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%l after <%n>",
+ start, p - start + 1, id);
+ }
+ CHECKNAMEARG(start, p - start + 1);
+ get_hash(mrb, &hash, argc, argv);
+ id = mrb_intern_check(mrb, start + 1, p - start - 1);
+ if (id) {
+ nextvalue = mrb_hash_fetch(mrb, hash, mrb_symbol_value(id), mrb_undef_value());
}
- 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 (!id || mrb_undef_p(nextvalue)) {
+ mrb_raisef(mrb, E_KEY_ERROR, "key%l not found", start, p - start + 1);
}
if (term == '}') goto format_s;
p++;
@@ -670,7 +709,6 @@ retry:
}
flags |= FPREC|FPREC0;
- prec = 0;
p++;
if (*p == '*') {
GETASTER(prec);
@@ -680,7 +718,6 @@ retry:
p++;
goto retry;
}
-
GETNUM(prec, precision);
goto retry;
@@ -706,9 +743,14 @@ retry:
mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character");
}
}
- else if (mrb_fixnum_p(val)) {
- mrb_int n = mrb_fixnum(val);
-
+ else if (mrb_integer_p(val)) {
+ mrb_int n = mrb_integer(val);
+#ifndef MRB_UTF8_STRING
+ char buf[1];
+
+ buf[0] = (char)n&0xff;
+ tmp = mrb_str_new(mrb, buf, 1);
+#else
if (n < 0x80) {
char buf[1];
@@ -716,9 +758,10 @@ retry:
tmp = mrb_str_new(mrb, buf, 1);
}
else {
- tmp = mrb_funcall(mrb, val, "chr", 0);
+ tmp = mrb_funcall_id(mrb, val, MRB_SYM(chr), 0);
mrb_check_type(mrb, tmp, MRB_TT_STRING);
}
+#endif
}
else {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character");
@@ -794,7 +837,7 @@ retry:
case 'B':
case 'u': {
mrb_value val = GETARG();
- char nbuf[68], *s;
+ char nbuf[69], *s;
const char *prefix = NULL;
int sign = 0, dots = 0;
char sc = 0;
@@ -815,21 +858,20 @@ retry:
bin_retry:
switch (mrb_type(val)) {
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
- val = mrb_flo_to_fixnum(mrb, val);
- if (mrb_fixnum_p(val)) goto bin_retry;
- break;
+ val = mrb_float_to_integer(mrb, val);
+ goto bin_retry;
#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);
+ case MRB_TT_INTEGER:
+ v = mrb_integer(val);
break;
default:
- val = mrb_Integer(mrb, val);
- goto bin_retry;
+ v = mrb_as_int(mrb, val);
+ break;
}
switch (*p) {
@@ -845,6 +887,7 @@ retry:
case 'd':
case 'i':
sign = 1;
+ /* fall through */
default:
base = 10; break;
}
@@ -864,48 +907,15 @@ retry:
sc = '-';
width--;
}
- mrb_assert(base == 10);
- snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v);
- s = nbuf;
+ s = mrb_int_to_cstr(nbuf, sizeof(nbuf), v, base);
if (v < 0) s++; /* skip minus sign */
}
else {
- s = nbuf;
+ /* print as unsigned */
+ s = mrb_uint_to_cstr(nbuf, sizeof(nbuf), v, base);
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;
@@ -993,22 +1003,17 @@ retry:
}
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case 'f':
case 'g':
case 'G':
case 'e':
- case 'E':
- case 'a':
- case 'A': {
+ case 'E': {
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));
+ fval = mrb_as_float(mrb, val);
if (!isfinite(fval)) {
const char *expr;
const mrb_int elen = 3;
@@ -1047,12 +1052,10 @@ retry:
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;
+ int i;
+ frexp(fval, &i);
if (i > 0)
need = BIT_DIGITS(i);
}
@@ -1070,7 +1073,7 @@ retry:
need += 20;
CHECK(need);
- n = snprintf(&buf[blen], need, fbuf, fval);
+ n = fmt_float(&buf[blen], need, *p, flags, width, prec, fval);
if (n < 0 || n >= need) {
mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
}
@@ -1089,7 +1092,7 @@ retry:
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));
+ if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%s", mesg);
}
#endif
mrb_str_resize(mrb, result, blen);
@@ -1097,31 +1100,16 @@ retry:
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;
- }
+void
+mrb_mruby_sprintf_gem_init(mrb_state *mrb)
+{
+ struct RClass *krn = mrb->kernel_module;
+ mrb_define_module_function(mrb, krn, "sprintf", mrb_f_sprintf, MRB_ARGS_ANY());
+ mrb_define_module_function(mrb, krn, "format", mrb_f_sprintf, MRB_ARGS_ANY());
+}
- *buf++ = c;
- *buf = '\0';
+void
+mrb_mruby_sprintf_gem_final(mrb_state *mrb)
+{
}
-#endif
diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb
index a5fd4e638..aedc2787a 100644
--- a/mrbgems/mruby-sprintf/test/sprintf.rb
+++ b/mrbgems/mruby-sprintf/test/sprintf.rb
@@ -1,15 +1,24 @@
-#assert('Kernel.sprintf') do
-#end
+assert('sprintf invalid') do
+ assert_raise(ArgumentError) { sprintf('%1$*d', 3) }
+ assert_raise(ArgumentError) { sprintf('%1$.*d', 3) }
+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} < %<str>s" % { num: 123, str: "456" }
assert_equal 15, ("%b" % (1<<14)).size
+ skip unless Object.const_defined?(:Float)
+ assert_equal "1.0", "%3.1f" % 1.01
+ assert_equal " 12345.12", "% 4.2f" % 12345.1234
+ assert_equal "12345.12", "%-4.2f" % 12345.12345
+ assert_equal "+12345.12", "%+4.2f" % 12345.1234
+ assert_equal "12345.12", "%04.2f" % 12345.12345
+ assert_equal "0012345.12", "%010.2f" % 12345.1234
end
assert('String#% with inf') do
+ skip unless Object.const_defined?(:Float)
inf = Float::INFINITY
assert_equal "Inf", "%f" % inf
@@ -35,9 +44,10 @@ assert('String#% with inf') do
assert_equal " Inf", "% 3f" % inf
assert_equal " Inf", "% 4f" % inf
assert_equal " Inf", "% 5f" % inf
-end if class_defined?("Float")
+end
assert('String#% with nan') do
+ skip unless Object.const_defined?(:Float)
nan = Float::NAN
assert_equal "NaN", "%f" % nan
@@ -63,29 +73,6 @@ assert('String#% with nan') do
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
diff --git a/mrbgems/mruby-string-ext/mrbgem.rake b/mrbgems/mruby-string-ext/mrbgem.rake
index 9812f2cc9..f2df5a783 100644
--- a/mrbgems/mruby-string-ext/mrbgem.rake
+++ b/mrbgems/mruby-string-ext/mrbgem.rake
@@ -2,5 +2,4 @@ MRuby::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
diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index 27ca30610..2b3071567 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -1,25 +1,6 @@
class String
##
- # call-seq:
- # String.try_convert(obj) -> string or nil
- #
- # Try to convert <i>obj</i> into a String, using to_str method.
- # Returns converted string or nil if <i>obj</i> cannot be converted
- # for any reason.
- #
- # String.try_convert("str") #=> "str"
- # String.try_convert(/re/) #=> nil
- #
- def self.try_convert(obj)
- if obj.respond_to?(:to_str)
- obj.to_str
- else
- nil
- end
- end
-
- ##
# call-seq:
# string.clear -> string
#
@@ -112,7 +93,7 @@ class String
# "hello".rstrip! #=> nil
#
def rstrip!
- raise RuntimeError, "can't modify frozen String" if frozen?
+ raise FrozenError, "can't modify frozen String" if frozen?
s = self.rstrip
(s == self) ? nil : self.replace(s)
end
@@ -142,7 +123,7 @@ class String
# "abcdef".casecmp("ABCDEF") #=> 0
#
def casecmp(str)
- self.downcase <=> str.to_str.downcase
+ self.downcase <=> str.__to_str.downcase
rescue NoMethodError
nil
end
@@ -167,7 +148,7 @@ class String
m = n + sep.size
[ slice(0, n), sep, slice(m, size - m) ]
else
- [ self, "", "" ]
+ [ self[0..-1], "", "" ]
end
end
@@ -294,8 +275,8 @@ class String
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)]
+ pad_repetitions = idx / padstr.size
+ padding = (padstr * pad_repetitions)[0, idx-self.size]
self + padding
end
@@ -313,11 +294,31 @@ class String
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)]
+ pad_repetitions = idx / padstr.size
+ padding = (padstr * pad_repetitions)[0, idx-self.size]
padding + self
end
+ ##
+ # call-seq:
+ # str.center(width, padstr=' ') -> new_str
+ #
+ # Centers +str+ in +width+. If +width+ is greater than the length of +str+,
+ # returns a new String of length +width+ with +str+ centered and padded with
+ # +padstr+; otherwise, returns +str+.
+ #
+ # "hello".center(4) #=> "hello"
+ # "hello".center(20) #=> " hello "
+ # "hello".center(20, '123') #=> "1231231hello12312312"
+ def center(width, padstr = ' ')
+ raise ArgumentError, 'zero width padding' if padstr == ''
+ return self if width <= self.size
+ width -= self.size
+ pad1 = width / 2
+ pad2 = width - pad1
+ (padstr*pad1)[0,pad1] + self + (padstr*pad2)[0,pad2]
+ end
+
def chars(&block)
if block_given?
self.split('').each do |i|
@@ -329,18 +330,20 @@ class String
end
end
+ ##
+ # Call the given block for each character of
+ # +self+.
def each_char(&block)
return to_enum :each_char unless block
-
- split('').each do |i|
- block.call(i)
+ pos = 0
+ while pos < self.size
+ block.call(self[pos])
+ pos += 1
end
self
end
def codepoints(&block)
- len = self.size
-
if block_given?
self.split('').each do|x|
block.call(x.ord)
@@ -429,7 +432,7 @@ class String
e = max.ord
while c <= e
break if exclusive and c == e
- yield c.chr
+ yield c.chr(__ENCODING__)
c += 1
end
return self
@@ -437,7 +440,6 @@ class String
# 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
@@ -455,6 +457,29 @@ class String
break if exclusive and n == 0
yield bs
break if n == 0
+ bsiz = bs.size
+ break if bsiz > max.size || bsiz == 0
+ bs = bs.succ
+ end
+ self
+ end
+
+ def __upto_endless(&block)
+ len = self.length
+ # both edges are all digits
+ bi = self.to_i(10)
+ if bi > 0 or bi == "0"*len
+ while true
+ 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
+ yield bs
bs = bs.succ
end
self
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index 9e35b18a9..f5f46c082 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -5,82 +5,90 @@
#include <mruby/string.h>
#include <mruby/range.h>
-static mrb_value
-mrb_str_getbyte(mrb_state *mrb, mrb_value str)
-{
- mrb_int pos;
- mrb_get_args(mrb, "i", &pos);
+#define ENC_ASCII_8BIT "ASCII-8BIT"
+#define ENC_BINARY "BINARY"
+#define ENC_UTF8 "UTF-8"
- if (pos < 0)
- pos += RSTRING_LEN(str);
- if (pos < 0 || RSTRING_LEN(str) <= pos)
- return mrb_nil_value();
+#define ENC_COMP_P(enc, enc_lit) \
+ str_casecmp_p(RSTRING_PTR(enc), RSTRING_LEN(enc), enc_lit, sizeof(enc_lit"")-1)
- return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]);
+#ifdef MRB_NO_FLOAT
+# define mrb_float_p(o) FALSE
+#endif
+
+static mrb_bool
+str_casecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2)
+{
+ const char *e1, *e2;
+
+ if (len1 != len2) return FALSE;
+ e1 = s1 + len1;
+ e2 = s2 + len2;
+ while (s1 < e1 && s2 < e2) {
+ if (*s1 != *s2 && TOUPPER(*s1) != TOUPPER(*s2)) return FALSE;
+ ++s1;
+ ++s2;
+ }
+ return TRUE;
}
static mrb_value
-mrb_str_setbyte(mrb_state *mrb, mrb_value str)
+int_chr_binary(mrb_state *mrb, mrb_value num)
{
- 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_int cp = mrb_as_int(mrb, num);
+ char c;
+ mrb_value str;
- mrb_str_modify(mrb, mrb_str_ptr(str));
- byte &= 0xff;
- RSTRING_PTR(str)[pos] = (unsigned char)byte;
- return mrb_fixnum_value((unsigned char)byte);
+ if (cp < 0 || 0xff < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%v out of char range", num);
+ }
+ c = (char)cp;
+ str = mrb_str_new(mrb, &c, 1);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
+#ifdef MRB_UTF8_STRING
static mrb_value
-mrb_str_byteslice(mrb_state *mrb, mrb_value str)
+int_chr_utf8(mrb_state *mrb, mrb_value num)
{
- mrb_value a1;
+ mrb_int cp = mrb_int(mrb, num);
+ char utf8[4];
mrb_int len;
+ mrb_value str;
+ uint32_t ascii_flag = 0;
- if (mrb_get_argc(mrb) == 2) {
- mrb_int pos;
- mrb_get_args(mrb, "ii", &pos, &len);
- return mrb_str_substr(mrb, str, pos, len);
+ if (cp < 0 || 0x10FFFF < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%v out of char range", num);
}
- 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");
+ if (cp < 0x80) {
+ utf8[0] = (char)cp;
+ len = 1;
+ ascii_flag = MRB_STR_ASCII;
}
- /* not reached */
- return mrb_nil_value();
+ 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;
+ }
+ str = mrb_str_new(mrb, utf8, len);
+ mrb_str_ptr(str)->flags |= ascii_flag;
+ return str;
}
+#endif
/*
* call-seq:
@@ -137,8 +145,6 @@ mrb_str_swapcase(mrb_state *mrb, mrb_value self)
return str;
}
-static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
-
/*
* call-seq:
* str << integer -> str
@@ -148,7 +154,8 @@ static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
*
* Append---Concatenates the given object to <i>str</i>. If the object is a
* <code>Integer</code>, it is considered as a codepoint, and is converted
- * to a character before concatenation.
+ * to a character before concatenation
+ * (equivalent to <code>str.concat(integer.chr(__ENCODING__))</code>).
*
* a = "hello "
* a << "world" #=> "hello world"
@@ -157,14 +164,17 @@ static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
static mrb_value
mrb_str_concat_m(mrb_state *mrb, mrb_value self)
{
- mrb_value str;
+ mrb_value str = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &str);
- if (mrb_fixnum_p(str))
- str = mrb_fixnum_chr(mrb, str);
+ if (mrb_integer_p(str) || mrb_float_p(str))
+#ifdef MRB_UTF8_STRING
+ str = int_chr_utf8(mrb, str);
+#else
+ str = int_chr_binary(mrb, str);
+#endif
else
- str = mrb_string_type(mrb, str);
- mrb_str_concat(mrb, self, str);
+ mrb_ensure_string_type(mrb, str);
+ mrb_str_cat_str(mrb, self, str);
return self;
}
@@ -184,14 +194,15 @@ mrb_str_concat_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_str_start_with(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv, sub;
+ const mrb_value *argv;
+ mrb_value 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_string_type(mrb, argv[i]);
+ sub = mrb_ensure_string_type(mrb, argv[i]);
mrb_gc_arena_restore(mrb, ai);
len_l = RSTRING_LEN(self);
len_r = RSTRING_LEN(sub);
@@ -213,14 +224,15 @@ mrb_str_start_with(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_str_end_with(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv, sub;
+ const mrb_value *argv;
+ mrb_value 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_string_type(mrb, argv[i]);
+ sub = mrb_ensure_string_type(mrb, argv[i]);
mrb_gc_arena_restore(mrb, ai);
len_l = RSTRING_LEN(self);
len_r = RSTRING_LEN(sub);
@@ -235,6 +247,12 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
+enum tr_pattern_type {
+ TR_UNINITIALIZED = 0,
+ TR_IN_ORDER = 1,
+ TR_RANGE = 2,
+};
+
/*
#tr Pattern syntax
@@ -244,19 +262,27 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
<range> ::= <ch> '-' <ch>
*/
struct tr_pattern {
- uint8_t type; // 1:in-order, 2:range
- mrb_bool flag_reverse;
- int16_t n;
+ 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;
- char ch[];
};
-static void
+#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;
- mrb_free(mrb, pat);
+ if (pat->flag_on_heap) {
+ mrb_free(mrb, pat);
+ }
pat = p;
}
}
@@ -265,10 +291,10 @@ 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);
- int pattern_length = RSTRING_LEN(v_pattern);
+ mrb_int pattern_length = RSTRING_LEN(v_pattern);
mrb_bool flag_reverse = FALSE;
struct tr_pattern *pat1;
- int i = 0;
+ mrb_int i = 0;
if(flag_reverse_enable && pattern_length >= 2 && pattern[0] == '^') {
flag_reverse = TRUE;
@@ -277,46 +303,53 @@ tr_parse_pattern(mrb_state *mrb, struct tr_pattern *ret, const mrb_value v_patte
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] == '-') {
- pat1 = (struct tr_pattern*)mrb_malloc_simple(mrb, sizeof(struct tr_pattern) + 2);
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 = 2;
+ 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->ch[0] = pattern[i];
- pat1->ch[1] = pattern[i+2];
+ pat1->val.ch[0] = pattern[i];
+ pat1->val.ch[1] = pattern[i+2];
i += 3;
}
else {
/* in order pattern. */
- int start_pos = i++;
- int len;
+ mrb_int start_pos = i++;
+ mrb_int len;
while (i < pattern_length) {
- if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-')
+ if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-')
break;
- i++;
+ i++;
}
len = i - start_pos;
- pat1 = (struct tr_pattern*)mrb_malloc_simple(mrb, sizeof(struct tr_pattern) + len);
+ if (len > UINT16_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "tr pattern too long (max 65535)");
+ }
if (pat1 == NULL && ret) {
goto nomem;
}
- pat1->type = 1;
+ pat1->type = TR_IN_ORDER;
pat1->flag_reverse = flag_reverse;
- pat1->n = len;
+ pat1->flag_on_heap = !ret_uninit;
+ pat1->n = (uint16_t)len;
pat1->next = NULL;
- memcpy(pat1->ch, &pattern[start_pos], len);
+ pat1->val.start_pos = (uint16_t)start_pos;
}
- if (ret == NULL) {
+ if (ret == NULL || ret_uninit) {
ret = pat1;
}
else {
@@ -331,23 +364,26 @@ tr_parse_pattern(mrb_state *mrb, struct tr_pattern *ret, const mrb_value v_patte
return ret;
}
-static mrb_int
-tr_find_character(const struct tr_pattern *pat, int ch)
+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 == 1) { /* pat->type == 1 in-order */
+ if (pat->type == TR_IN_ORDER) {
int i;
for (i = 0; i < pat->n; i++) {
- if (pat->ch[i] == ch) ret = n_sum + i;
+ if (pat_str[pat->val.start_pos + i] == ch) ret = n_sum + i;
}
}
- else { /* pat->type == 2 range */
- if (pat->ch[0] <= ch && ch <= pat->ch[1])
- ret = n_sum + ch - pat->ch[0];
+ 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;
@@ -359,17 +395,33 @@ tr_find_character(const struct tr_pattern *pat, int ch)
return ret;
}
-static mrb_int
-tr_get_character(const struct tr_pattern *pat, mrb_int n_th)
+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);
- return (pat->type == 1) ? pat->ch[i] :pat->ch[0] + i;
+
+ 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) {
- return (pat->type == 1) ? pat->ch[pat->n - 1] : pat->ch[1];
+ 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;
@@ -378,51 +430,105 @@ tr_get_character(const struct tr_pattern *pat, mrb_int n_th)
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<<idx2);
+}
+
+static inline mrb_bool
+tr_bitmap_detect(uint8_t bitmap[32], uint8_t ch)
+{
+ uint8_t idx1 = ch / 8;
+ uint8_t idx2 = ch % 8;
+ if (bitmap[idx1] & (1<<idx2))
+ return TRUE;
+ return FALSE;
+}
+
+/* compile patter to bitmap */
+static void
+tr_compile_pattern(const struct tr_pattern *pat, mrb_value pstr, uint8_t bitmap[32])
+{
+ const char *pattern = RSTRING_PTR(pstr);
+ mrb_int flag_reverse = pat ? pat->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;
- struct tr_pattern *rep;
+ 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));
- pat = tr_parse_pattern(mrb, NULL, p1, TRUE);
- rep = tr_parse_pattern(mrb, NULL, p2, FALSE);
+ 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; i<len; i++,j++) {
- mrb_int n = tr_find_character(pat, s[i]);
+ mrb_int n = tr_find_character(&pat, RSTRING_PTR(p1), s[i]);
if (i>j) s[j] = s[i];
if (n >= 0) {
flag_changed = TRUE;
if (rep == NULL) {
- j--;
+ j--;
}
else {
- mrb_int c = tr_get_character(rep, n);
+ mrb_int c = tr_get_character(rep, RSTRING_PTR(p2), n);
- if (squeeze && c == lastch) {
+ if (c < 0 || (squeeze && c == lastch)) {
j--;
continue;
}
- if (c < 0 || c > 0x80) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%S) out of range",
- mrb_fixnum_value((mrb_int)c));
+ if (c > 0x80) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%i) out of range", c);
}
- lastch = s[i] = c;
+ lastch = c;
+ s[i] = (char)c;
}
}
}
- tr_free_pattern(mrb, pat);
- if (rep) tr_free_pattern(mrb, rep);
+ tr_free_pattern(mrb, &pat);
+ tr_free_pattern(mrb, rep);
if (flag_changed) {
RSTR_SET_LEN(RSTRING(str), j);
@@ -544,26 +650,28 @@ mrb_str_tr_s_bang(mrb_state *mrb, mrb_value str)
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, v_pat, TRUE);
+ 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; i<len; i++,j++) {
- mrb_int n = tr_find_character(pat, s[i]);
-
if (i>j) s[j] = s[i];
- if (n >= 0 && s[i] == lastch) {
+ if (tr_bitmap_detect(bitmap, s[i]) && s[i] == lastch) {
flag_changed = TRUE;
j--;
}
@@ -580,7 +688,6 @@ str_squeeze(mrb_state *mrb, mrb_value str, mrb_value v_pat)
lastch = s[i];
}
}
- tr_free_pattern(mrb, pat);
if (flag_changed) {
RSTR_SET_LEN(RSTRING(str), j);
@@ -637,27 +744,28 @@ mrb_str_squeeze_bang(mrb_state *mrb, mrb_value str)
static mrb_bool
str_delete(mrb_state *mrb, mrb_value str, mrb_value v_pat)
{
- struct tr_pattern *pat = NULL;
+ 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));
- pat = tr_parse_pattern(mrb, pat, v_pat, TRUE);
+ 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; i<len; i++,j++) {
- mrb_int n = tr_find_character(pat, s[i]);
-
if (i>j) s[j] = s[i];
- if (n >= 0) {
+ if (tr_bitmap_detect(bitmap, s[i])) {
flag_changed = TRUE;
j--;
}
}
- tr_free_pattern(mrb, pat);
if (flag_changed) {
RSTR_SET_LEN(RSTRING(str), j);
RSTRING_PTR(str)[j] = 0;
@@ -704,22 +812,23 @@ static mrb_value
mrb_str_count(mrb_state *mrb, mrb_value str)
{
mrb_value v_pat = mrb_nil_value();
- struct tr_pattern *pat = NULL;
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);
- pat = tr_parse_pattern(mrb, pat, v_pat, TRUE);
+ 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++) {
- mrb_int n = tr_find_character(pat, s[i]);
-
- if (n >= 0) count++;
+ if (tr_bitmap_detect(bitmap, s[i])) count++;
}
- tr_free_pattern(mrb, pat);
return mrb_fixnum_value(count);
}
@@ -750,49 +859,42 @@ mrb_str_chr(mrb_state *mrb, mrb_value self)
return mrb_str_substr(mrb, self, 0, 1);
}
+/*
+ * call-seq:
+ * int.chr([encoding]) -> string
+ *
+ * Returns a string containing the character represented by the +int+'s value
+ * according to +encoding+. +"ASCII-8BIT"+ (+"BINARY"+) and +"UTF-8"+ (only
+ * with +MRB_UTF8_STRING+) can be specified as +encoding+ (default is
+ * +"ASCII-8BIT"+).
+ *
+ * 65.chr #=> "A"
+ * 230.chr #=> "\xE6"
+ * 230.chr("ASCII-8BIT") #=> "\xE6"
+ * 230.chr("UTF-8") #=> "\u00E6"
+ */
static mrb_value
-mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
+mrb_int_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;
+ mrb_value enc;
+ mrb_bool enc_given;
+
+ mrb_get_args(mrb, "|S?", &enc, &enc_given);
+ if (!enc_given ||
+ ENC_COMP_P(enc, ENC_ASCII_8BIT) ||
+ ENC_COMP_P(enc, ENC_BINARY)) {
+ return int_chr_binary(mrb, num);
}
- 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;
+#ifdef MRB_UTF8_STRING
+ else if (ENC_COMP_P(enc, ENC_UTF8)) {
+ return int_chr_utf8(mrb, num);
}
+#endif
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;
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown encoding name - %v", enc);
}
- 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
+ /* not reached */
+ return mrb_nil_value();
}
/*
@@ -972,7 +1074,8 @@ static mrb_value
mrb_str_del_prefix_bang(mrb_state *mrb, mrb_value self)
{
mrb_int plen, slen;
- char *ptr, *s;
+ const char *ptr;
+ char *s;
struct RString *str = RSTRING(self);
mrb_get_args(mrb, "s", &ptr, &plen);
@@ -980,7 +1083,7 @@ mrb_str_del_prefix_bang(mrb_state *mrb, mrb_value self)
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))) {
+ if (!mrb_frozen_p(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) {
str->as.heap.ptr += plen;
}
else {
@@ -1005,7 +1108,7 @@ static mrb_value
mrb_str_del_prefix(mrb_state *mrb, mrb_value self)
{
mrb_int plen, slen;
- char *ptr;
+ const char *ptr;
mrb_get_args(mrb, "s", &ptr, &plen);
slen = RSTRING_LEN(self);
@@ -1029,7 +1132,8 @@ static mrb_value
mrb_str_del_suffix_bang(mrb_state *mrb, mrb_value self)
{
mrb_int plen, slen;
- char *ptr, *s;
+ const char *ptr;
+ char *s;
struct RString *str = RSTRING(self);
mrb_get_args(mrb, "s", &ptr, &plen);
@@ -1037,7 +1141,7 @@ mrb_str_del_suffix_bang(mrb_state *mrb, mrb_value self)
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))) {
+ if (!mrb_frozen_p(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) {
/* no need to modify string */
}
else {
@@ -1060,7 +1164,7 @@ static mrb_value
mrb_str_del_suffix(mrb_state *mrb, mrb_value self)
{
mrb_int plen, slen;
- char *ptr;
+ const char *ptr;
mrb_get_args(mrb, "s", &ptr, &plen);
slen = RSTRING_LEN(self);
@@ -1080,8 +1184,7 @@ mrb_str_lines(mrb_state *mrb, mrb_value self)
char *p = b, *t;
char *e = b + RSTRING_LEN(self);
- mrb_get_args(mrb, "");
-
+ mrb->c->ci->mid = 0;
result = mrb_ary_new(mrb);
ai = mrb_gc_arena_save(mrb);
while (p < e) {
@@ -1101,9 +1204,6 @@ 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));
@@ -1124,8 +1224,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
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, "next", mrb_str_succ, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "next!", mrb_str_succ_bang, MRB_ARGS_NONE());
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));
@@ -1133,7 +1233,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
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());
+
+ mrb_define_method(mrb, mrb_class_get(mrb, "Integer"), "chr", mrb_int_chr, MRB_ARGS_OPT(1));
}
void
diff --git a/mrbgems/mruby-string-ext/test/numeric.rb b/mrbgems/mruby-string-ext/test/numeric.rb
new file mode 100644
index 000000000..dfcb9ebf4
--- /dev/null
+++ b/mrbgems/mruby-string-ext/test/numeric.rb
@@ -0,0 +1,29 @@
+# coding: utf-8
+
+assert('Integer#chr') do
+ assert_equal("A", 65.chr)
+ assert_equal("B", 0x42.chr)
+ assert_equal("\xab", 171.chr)
+ assert_raise(RangeError) { -1.chr }
+ assert_raise(RangeError) { 256.chr }
+
+ assert_equal("A", 65.chr("ASCII-8BIT"))
+ assert_equal("B", 0x42.chr("BINARY"))
+ assert_equal("\xab", 171.chr("ascii-8bit"))
+ assert_raise(RangeError) { -1.chr("binary") }
+ assert_raise(RangeError) { 256.chr("Ascii-8bit") }
+ assert_raise(ArgumentError) { 65.chr("ASCII") }
+ assert_raise(ArgumentError) { 65.chr("ASCII-8BIT", 2) }
+ assert_raise(TypeError) { 65.chr(:BINARY) }
+
+ if __ENCODING__ == "ASCII-8BIT"
+ assert_raise(ArgumentError) { 65.chr("UTF-8") }
+ else
+ assert_equal("A", 65.chr("UTF-8"))
+ assert_equal("B", 0x42.chr("UTF-8"))
+ assert_equal("«", 171.chr("utf-8"))
+ assert_equal("あ", 12354.chr("Utf-8"))
+ assert_raise(RangeError) { -1.chr("utf-8") }
+ assert_raise(RangeError) { 0x110000.chr.chr("UTF-8") }
+ end
+end
diff --git a/mrbgems/mruby-string-ext/test/range.rb b/mrbgems/mruby-string-ext/test/range.rb
new file mode 100644
index 000000000..80c286850
--- /dev/null
+++ b/mrbgems/mruby-string-ext/test/range.rb
@@ -0,0 +1,26 @@
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 'l', ('f'..'l').max
+ assert_equal 'e', ('a'...'f').max
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ('z'..'l').max
+end
+
+assert('Range#max given a block') do
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (('z'..'l').max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 'f', ('f'..'l').min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ('z'..'l').min
+end
+
+assert('Range#min given a block') do
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (('z'..'l').min { |x, y| x <=> y })
+end
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index 36a253989..7be673aa6 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -2,112 +2,82 @@
##
# String(Ext) Test
-UTF8STRING = ("\343\201\202".size == 1)
-
-assert('String.try_convert') do
- assert_nil String.try_convert(nil)
- assert_nil String.try_convert(:foo)
- assert_equal "", String.try_convert("")
- assert_equal "1,2,3", String.try_convert("1,2,3")
-end
-
-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#setbyte raises IndexError if arg conversion resizes String") do
- $s = "01234\n"
- class Tmp
- def to_i
- $s.chomp! ''
- 95
- end
- end
- tmp = Tmp.new
- assert_raise(IndexError) { $s.setbyte(5, tmp) }
-end
+UTF8STRING = __ENCODING__ == "UTF-8"
-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))
+def assert_upto(exp, receiver, *args)
+ act = []
+ receiver.upto(*args) { |v| act << v }
+ assert_equal exp, act
end
assert('String#dump') do
- ("\1" * 100).dump # should not raise an exception - regress #1210
- "\0".inspect == "\"\\000\"" and
- "foo".dump == "\"foo\""
+ assert_equal("\"\\x00\"", "\0".dump)
+ assert_equal("\"foo\"", "foo".dump)
+ assert_equal('"\xe3\x82\x8b"', "る".dump)
+ assert_nothing_raised { ("\1" * 100).dump } # regress #1210
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 "
+ assert_equal("abc", s.strip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".strip)
+ assert_equal("", " \t\r\n\f\v".strip)
+ assert_equal("\0a", "\0a\0".strip)
+ assert_equal("abc", "abc".strip)
+ assert_equal("abc", " abc".strip)
+ assert_equal("abc", "abc ".strip)
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 "
+ assert_equal("abc ", s.lstrip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".lstrip)
+ assert_equal("", " \t\r\n\f\v".lstrip)
+ assert_equal("\0a\0", "\0a\0".lstrip)
+ assert_equal("abc", "abc".lstrip)
+ assert_equal("abc", " abc".lstrip)
+ assert_equal("abc ", "abc ".lstrip)
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 "
+ assert_equal(" abc", s.rstrip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".rstrip)
+ assert_equal("", " \t\r\n\f\v".rstrip)
+ assert_equal("\0a", "\0a\0".rstrip)
+ assert_equal("abc", "abc".rstrip)
+ assert_equal(" abc", " abc".rstrip)
+ assert_equal("abc", "abc ".rstrip)
end
assert('String#strip!') do
s = " abc "
t = "abc"
- s.strip! == "abc" and s == "abc" and t.strip! == nil
+ assert_equal("abc", s.strip!)
+ assert_equal("abc", s)
+ assert_nil(t.strip!)
+ assert_equal("abc", t)
end
assert('String#lstrip!') do
s = " abc "
t = "abc "
- s.lstrip! == "abc " and s == "abc " and t.lstrip! == nil
+ assert_equal("abc ", s.lstrip!)
+ assert_equal("abc ", s)
+ assert_nil(t.lstrip!)
+ assert_equal("abc ", t)
end
assert('String#rstrip!') do
s = " abc "
t = " abc"
- s.rstrip! == " abc" and s == " abc" and t.rstrip! == nil
+ assert_equal(" abc", s.rstrip!)
+ assert_equal(" abc", s)
+ assert_nil(t.rstrip!)
+ assert_equal(" abc", t)
end
assert('String#swapcase') do
@@ -125,14 +95,15 @@ 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) }
- o = Object.new
- def o.to_str
- "to_str"
+ if UTF8STRING
+ assert_equal "H«", "H" << 0xab
+ assert_equal "Hは", "H" << 12399
+ else
+ assert_equal "H\xab", "H" << 0xab
+ assert_raise(RangeError) { "H" << 12399 }
end
- assert_equal "hi to_str", "hi " << o
-
- assert_raise(TypeError) { "".concat(Object.new) }
end
assert('String#casecmp') do
@@ -140,15 +111,11 @@ assert('String#casecmp') do
assert_equal 0, "aBcDeF".casecmp("abcdef")
assert_equal(-1, "abcdef".casecmp("abcdefg"))
assert_equal 0, "abcdef".casecmp("ABCDEF")
- o = Object.new
- def o.to_str
- "ABCDEF"
- end
- assert_equal 0, "abcdef".casecmp(o)
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")
@@ -156,11 +123,12 @@ assert('String#count') do
assert_equal 4, s.count("a0-9")
end
-assert('String#tr') do
+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
@@ -169,7 +137,7 @@ assert('String#tr!') do
assert_equal "ab12222hijklmnopqR", s
end
-assert('String#tr_s') do
+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')
@@ -265,12 +233,6 @@ assert('String#oct') do
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
@@ -512,6 +474,14 @@ assert('String#rjust') do
assert_equal "hello", "hello".rjust(-3)
end
+assert('String#center') do
+ assert_equal "hello", "hello".center(4)
+ assert_equal " hello ", "hello".center(20)
+ assert_equal 20, "hello".center(20).length
+ assert_equal "1231231hello12312312", "hello".center(20, '123')
+ assert_equal "hello", "hello".center(-3)
+end
+
if UTF8STRING
assert('String#ljust with UTF8') do
assert_equal "helloん ", "helloん".ljust(20)
@@ -557,16 +527,15 @@ assert('String#rjust should raise on zero width padding') do
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
+ assert_upto %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8", "b6"
+ assert_upto ["9", "10", "11"], "9", "11"
+ assert_upto [], "25", "5"
+ assert_upto ["07", "08", "09", "10", "11"], "07", "11"
+ assert_upto ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9", "A"
+
+ if UTF8STRING
+ assert_upto %w(あ ぃ い ぅ う ぇ え ぉ お), "あ", "お"
+ end
a = "aa"
start = "aa"
@@ -648,8 +617,11 @@ assert('String#ord(UTF-8)') do
end if UTF8STRING
assert('String#chr') do
+ assert_equal "a", "abcde".chr
assert_equal "h", "hello!".chr
+ assert_equal "", "".chr
end
+
assert('String#chr(UTF-8)') do
assert_equal "こ", "こんにちは世界!".chr
end if UTF8STRING
@@ -675,19 +647,19 @@ assert('String#chars(UTF-8)') do
end if UTF8STRING
assert('String#each_char') do
- s = ""
+ chars = []
"hello!".each_char do |x|
- s += x
+ chars << x
end
- assert_equal "hello!", s
+ assert_equal ["h", "e", "l", "l", "o", "!"], chars
end
assert('String#each_char(UTF-8)') do
- s = ""
+ chars = []
"こんにちは世界!".each_char do |x|
- s += x
+ chars << x
end
- assert_equal "こんにちは世界!", s
+ assert_equal ["こ", "ん", "に", "ち", "は", "世", "界", "!"], chars
end if UTF8STRING
assert('String#codepoints') do
diff --git a/mrbgems/mruby-struct/mrblib/struct.rb b/mrbgems/mruby-struct/mrblib/struct.rb
index 7cf3dd3ab..2439e2a37 100644
--- a/mrbgems/mruby-struct/mrblib/struct.rb
+++ b/mrbgems/mruby-struct/mrblib/struct.rb
@@ -2,88 +2,83 @@
# Struct
#
# ISO 15.2.18
+class Struct
-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 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 pass the name and value of the respective
+ # 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
+ ##
+ # 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 = "#<struct "
- else
- str = "#<struct #{name} "
- end
- buf = []
- self.each_pair do |k,v|
- buf.push [k.to_s + "=" + v._inspect]
- end
- str + buf.join(", ") + ">"
+ def _inspect(recur_list)
+ return "#<struct #{self.class}:...>" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
+ name = self.class.to_s
+ if name[0] == "#"
+ str = "#<struct "
+ else
+ str = "#<struct #{name} "
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
- "#<struct #{self.class.to_s}:...>"
- end
+ buf = []
+ self.each_pair do |k,v|
+ buf.push k.to_s + "=" + v._inspect(recur_list)
end
+ str + buf.join(", ") + ">"
+ end
- ##
- # 15.2.18.4.11(x)
- #
- alias to_s inspect
+ ##
+ # 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
+ self._inspect({})
end
##
+ # 15.2.18.4.11(x)
+ #
+ alias to_s inspect
+
+ ##
# call-seq:
# hsh.dig(key,...) -> object
#
@@ -100,4 +95,3 @@ if Object.const_defined?(:Struct)
end
end
end
-
diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c
index b567a00d5..df76d7a33 100644
--- a/mrbgems/mruby-struct/src/struct.c
+++ b/mrbgems/mruby-struct/src/struct.c
@@ -13,6 +13,7 @@
#include <mruby/hash.h>
#include <mruby/range.h>
#include <mruby/proc.h>
+#include <mruby/presym.h>
#define RSTRUCT_LEN(st) RARRAY_LEN(st)
#define RSTRUCT_PTR(st) RARRAY_PTR(st)
@@ -20,7 +21,7 @@
static struct RClass *
struct_class(mrb_state *mrb)
{
- return mrb_class_get(mrb, "Struct");
+ return mrb_class_get_id(mrb, MRB_SYM(Struct));
}
static inline mrb_value
@@ -42,7 +43,7 @@ struct_ivar_get(mrb_state *mrb, mrb_value cls, mrb_sym id)
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__"));
+ mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), MRB_SYM(__members__));
if (mrb_nil_p(members)) {
mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
@@ -66,8 +67,8 @@ struct_members(mrb_state *mrb, mrb_value s)
}
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)));
+ "struct size differs (%i required %i given)",
+ RARRAY_LEN(members), RSTRUCT_LEN(s));
}
}
return members;
@@ -87,10 +88,7 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
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_check_frozen(mrb, mrb_basic_ptr(strct));
mrb_write_barrier(mrb, mrb_basic_ptr(strct));
}
@@ -116,7 +114,7 @@ mrb_struct_members(mrb_state *mrb, mrb_value 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_int i = mrb_integer(mrb_proc_cfunc_env_get(mrb, 0));
mrb_value *ptr = RSTRUCT_PTR(obj);
if (!ptr) return mrb_nil_value();
@@ -126,30 +124,39 @@ mrb_struct_ref(mrb_state *mrb, mrb_value obj)
static mrb_sym
mrb_id_attrset(mrb_state *mrb, mrb_sym id)
{
+#define ONSTACK_ALLOC_MAX 32
+#define ONSTACK_STRLEN_MAX (ONSTACK_ALLOC_MAX - 1) /* '=' character */
+
const char *name;
char *buf;
mrb_int len;
mrb_sym mid;
+ char onstack[ONSTACK_ALLOC_MAX];
- name = mrb_sym2name_len(mrb, id, &len);
- buf = (char *)mrb_malloc(mrb, (size_t)len+2);
+ name = mrb_sym_name_len(mrb, id, &len);
+ if (len > ONSTACK_STRLEN_MAX) {
+ buf = (char *)mrb_malloc(mrb, (size_t)len+1);
+ }
+ else {
+ buf = onstack;
+ }
memcpy(buf, name, (size_t)len);
buf[len] = '=';
- buf[len+1] = '\0';
mid = mrb_intern(mrb, buf, len+1);
- mrb_free(mrb, buf);
+ if (buf != onstack) {
+ 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_int i = mrb_integer(mrb_proc_cfunc_env_get(mrb, 0));
mrb_value *ptr;
- mrb_value val;
+ mrb_value val = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &val);
mrb_struct_modify(mrb, obj);
ptr = RSTRUCT_PTR(obj);
if (ptr == NULL || i >= RSTRUCT_LEN(obj)) {
@@ -161,20 +168,6 @@ mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
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)
{
@@ -185,19 +178,15 @@ make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c
for (i=0; i<len; i++) {
mrb_sym id = mrb_symbol(ptr_members[i]);
- const char *name = mrb_sym2name_len(mrb, id, NULL);
-
- if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
- mrb_method_t m;
- mrb_value at = mrb_fixnum_value(i);
- struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
- struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
- MRB_METHOD_FROM_PROC(m, aref);
- mrb_define_method_raw(mrb, c, id, m);
- MRB_METHOD_FROM_PROC(m, aset);
- mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
- mrb_gc_arena_restore(mrb, ai);
- }
+ mrb_method_t m;
+ mrb_value at = mrb_fixnum_value(i);
+ struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
+ struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
+ MRB_METHOD_FROM_PROC(m, aref);
+ mrb_define_method_raw(mrb, c, id, m);
+ MRB_METHOD_FROM_PROC(m, aset);
+ mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
+ mrb_gc_arena_restore(mrb, ai);
}
}
@@ -213,20 +202,20 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *kl
}
else {
/* old style: should we warn? */
- name = mrb_str_to_str(mrb, name);
+ mrb_to_str(mrb, name);
id = mrb_obj_to_sym(mrb, name);
- if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
- mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
+ if (!mrb_const_name_p(mrb, RSTRING_PTR(name), RSTRING_LEN(name))) {
+ mrb_name_error(mrb, id, "identifier %v needs to be constant", name);
}
if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
- mrb_warn(mrb, "redefining constant Struct::%S", name);
+ mrb_warn(mrb, "redefining constant Struct::%v", name);
mrb_const_remove(mrb, mrb_obj_value(klass), id);
}
c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
}
MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
nstr = mrb_obj_value(c);
- mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
+ mrb_iv_set(mrb, nstr, MRB_SYM(__members__), members);
mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
@@ -274,18 +263,18 @@ static mrb_value
mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
{
mrb_value name, rest;
- mrb_value *pargv;
+ const mrb_value *pargv;
mrb_int argcnt;
mrb_int i;
mrb_value b, st;
mrb_sym id;
- mrb_value *argv;
+ const 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");
+ mrb_argnum_error(mrb, argc, 1, -1);
}
else {
pargv = argv;
@@ -322,7 +311,7 @@ num_members(mrb_state *mrb, struct RClass *klass)
{
mrb_value members;
- members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
+ members = struct_ivar_get(mrb, mrb_obj_value(klass), MRB_SYM(__members__));
if (!mrb_array_p(members)) {
mrb_raise(mrb, E_TYPE_ERROR, "broken members");
}
@@ -333,7 +322,7 @@ num_members(mrb_state *mrb, struct RClass *klass)
/*
*/
static mrb_value
-mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self)
+mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value self)
{
struct RClass *klass = mrb_obj_class(mrb, self);
mrb_int i, n;
@@ -355,7 +344,7 @@ mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb
static mrb_value
mrb_struct_initialize(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc;
mrb_get_args(mrb, "*!", &argv, &argc);
@@ -367,9 +356,7 @@ mrb_struct_initialize(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
{
- mrb_value s;
-
- mrb_get_args(mrb, "o", &s);
+ mrb_value s = mrb_get_arg1(mrb);
if (mrb_obj_equal(mrb, copy, s)) return copy;
if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) {
@@ -399,23 +386,22 @@ struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
return ptr[i];
}
}
- mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "no member '%n' in struct", id);
return mrb_nil_value(); /* not reached */
}
static mrb_value
struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
{
- 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_int idx = i < 0 ? RSTRUCT_LEN(s) + i : i;
+
+ if (idx < 0)
mrb_raisef(mrb, E_INDEX_ERROR,
- "offset %S too large for struct(size:%S)",
- mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
- return RSTRUCT_PTR(s)[i];
+ "offset %i too small for struct(size:%i)", i, RSTRUCT_LEN(s));
+ if (RSTRUCT_LEN(s) <= idx)
+ mrb_raisef(mrb, E_INDEX_ERROR,
+ "offset %i too large for struct(size:%i)", i, RSTRUCT_LEN(s));
+ return RSTRUCT_PTR(s)[idx];
}
/* 15.2.18.4.2 */
@@ -440,21 +426,16 @@ struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
static mrb_value
mrb_struct_aref(mrb_state *mrb, mrb_value s)
{
- mrb_value idx;
+ mrb_value idx = mrb_get_arg1(mrb);
- 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;
+ mrb_sym sym = mrb_intern_str(mrb, idx);
+ idx = mrb_symbol_value(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));
+ return struct_aref_int(mrb, s, mrb_as_int(mrb, idx));
}
static mrb_value
@@ -475,7 +456,7 @@ mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
return val;
}
}
- mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "no member '%n' in struct", id);
return val; /* not reach */
}
@@ -511,28 +492,22 @@ mrb_struct_aset(mrb_state *mrb, mrb_value s)
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;
+ mrb_sym sym = mrb_intern_str(mrb, idx);
+ idx = mrb_symbol_value(sym);
}
if (mrb_symbol_p(idx)) {
return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
}
- i = mrb_int(mrb, idx);
+ i = mrb_as_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)));
+ "offset %i too small for struct(size:%i)", i, 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)));
+ "offset %i too large for struct(size:%i)", i, RSTRUCT_LEN(s));
}
mrb_struct_modify(mrb, s);
return RSTRUCT_PTR(s)[i] = val;
@@ -559,11 +534,10 @@ mrb_struct_aset(mrb_state *mrb, mrb_value s)
static mrb_value
mrb_struct_equal(mrb_state *mrb, mrb_value s)
{
- mrb_value s2;
+ mrb_value s2 = mrb_get_arg1(mrb);
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();
}
@@ -596,11 +570,10 @@ mrb_struct_equal(mrb_state *mrb, mrb_value s)
static mrb_value
mrb_struct_eql(mrb_state *mrb, mrb_value s)
{
- mrb_value s2;
+ mrb_value s2 = mrb_get_arg1(mrb);
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();
}
@@ -624,8 +597,8 @@ mrb_struct_eql(mrb_state *mrb, mrb_value s)
/*
* call-seq:
- * struct.length -> Fixnum
- * struct.size -> Fixnum
+ * struct.length -> Integer
+ * struct.size -> Integer
*
* Returns number of struct members.
*/
@@ -674,7 +647,7 @@ static mrb_value
mrb_struct_values_at(mrb_state *mrb, mrb_value self)
{
mrb_int argc;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_get_args(mrb, "*", &argv, &argc);
diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb
index 982e344e2..93930730b 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -116,9 +116,10 @@ assert('struct dup') do
end
assert('struct inspect') do
- c = Struct.new(:m1, :m2, :m3, :m4, :m5)
- cc = c.new(1,2,3,4,5)
- assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect
+ c = Struct.new(:m1, :m2, :m3, :m4, :m5, :recur)
+ cc = c.new(1,2,3,4,5,nil)
+ cc.recur = cc
+ assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5, recur=#<struct #{cc.class}:...>>", cc.inspect
end
assert('Struct#length, Struct#size') do
@@ -152,14 +153,14 @@ assert("Struct#dig") do
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
+# TODO: suppress redefining Struct warning during test
+# assert("Struct.new removes existing constant") do
+# 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
@@ -181,6 +182,10 @@ assert("Struct.new does not allow array") do
end
end
+assert("Struct.new does not allow invalid class name") do
+ assert_raise(NameError) { Struct.new("Test-", :a) }
+end
+
assert("Struct.new generates subclass of Struct") do
begin
original_struct = Struct
@@ -199,7 +204,7 @@ assert 'Struct#freeze' do
assert_equal :test, o.m
o.freeze
- assert_raise(RuntimeError) { o.m = :modify }
- assert_raise(RuntimeError) { o[:m] = :modify }
+ assert_raise(FrozenError) { o.m = :modify }
+ assert_raise(FrozenError) { o[:m] = :modify }
assert_equal :test, o.m
end
diff --git a/mrbgems/mruby-symbol-ext/src/symbol.c b/mrbgems/mruby-symbol-ext/src/symbol.c
index a992dbfce..460d2531d 100644
--- a/mrbgems/mruby-symbol-ext/src/symbol.c
+++ b/mrbgems/mruby-symbol-ext/src/symbol.c
@@ -1,11 +1,9 @@
#include <mruby.h>
-#include <mruby/khash.h>
#include <mruby/array.h>
-
-typedef struct symbol_name {
- size_t len;
- const char *name;
-} symbol_name;
+#include <mruby/string.h>
+#ifdef MRB_USE_ALL_SYMBOLS
+# include <mruby/presym.h>
+#endif
/*
* call-seq:
@@ -22,18 +20,23 @@ typedef struct symbol_name {
* :Tms, :getwd, :$=, :ThreadGroup,
* :wait2, :$>]
*/
+#ifdef MRB_USE_ALL_SYMBOLS
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<lim; i++) {
+ for (i=1; i<=MRB_PRESYM_MAX; i++) {
mrb_ary_push(mrb, ary, mrb_symbol_value(i));
}
+ for (i=1, lim=mrb->symidx+1; i<lim; i++) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(i+MRB_PRESYM_MAX));
+ }
return ary;
}
+#endif
/*
* call-seq:
@@ -45,7 +48,13 @@ static mrb_value
mrb_sym_length(mrb_state *mrb, mrb_value self)
{
mrb_int len;
- mrb_sym2name_len(mrb, mrb_symbol(self), &len);
+#ifdef MRB_UTF8_STRING
+ mrb_int byte_len;
+ const char *name = mrb_sym_name_len(mrb, mrb_symbol(self), &byte_len);
+ len = mrb_utf8_strlen(name, byte_len);
+#else
+ mrb_sym_name_len(mrb, mrb_symbol(self), &len);
+#endif
return mrb_fixnum_value(len);
}
@@ -53,7 +62,9 @@ void
mrb_mruby_symbol_ext_gem_init(mrb_state* mrb)
{
struct RClass *s = mrb->symbol_class;
+#ifdef MRB_USE_ALL_SYMBOLS
mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE());
+#endif
mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE());
}
diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb
index 63c1bd826..a4fb27733 100644
--- a/mrbgems/mruby-symbol-ext/test/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/test/symbol.rb
@@ -1,15 +1,27 @@
+# coding: utf-8
##
# 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
+if Symbol.respond_to?(:all_symbols)
+ 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
end
-assert("Symbol#length") do
- assert_equal 5, :hello.size
- assert_equal 5, :mruby.length
+%w[size length].each do |n|
+ assert("Symbol##{n}") do
+ assert_equal 5, :hello.__send__(n)
+ assert_equal 4, :"aA\0b".__send__(n)
+ if __ENCODING__ == "UTF-8"
+ assert_equal 8, :"こんにちは世界!".__send__(n)
+ assert_equal 4, :"aあ\0b".__send__(n)
+ else
+ assert_equal 22, :"こんにちは世界!".__send__(n)
+ assert_equal 6, :"aあ\0b".__send__(n)
+ end
+ end
end
assert("Symbol#capitalize") do
@@ -36,7 +48,7 @@ assert("Symbol#casecmp") do
end
assert("Symbol#empty?") do
- assert_true :''.empty?
+ assert_false :'a'.empty?
end
assert('Symbol#intern') do
diff --git a/mrbgems/mruby-test/README.md b/mrbgems/mruby-test/README.md
index fa4b91e3a..86b1ce05e 100644
--- a/mrbgems/mruby-test/README.md
+++ b/mrbgems/mruby-test/README.md
@@ -3,5 +3,6 @@ Running Tests
To run the tests, execute the following from the project's root directory.
- $ make test
-
+```
+$ make test
+```
diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c
index 434d1fee5..23f172198 100644
--- a/mrbgems/mruby-test/driver.c
+++ b/mrbgems/mruby-test/driver.c
@@ -18,8 +18,10 @@
#include <mruby/variable.h>
#include <mruby/array.h>
-void
-mrb_init_mrbtest(mrb_state *);
+extern const uint8_t mrbtest_assert_irep[];
+
+void mrbgemtest_init(mrb_state* mrb);
+void mrb_init_test_vformat(mrb_state* mrb);
/* Print a short remark for the user */
static void
@@ -29,56 +31,182 @@ print_hint(void)
}
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);
+ mrb_value result = 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;
+ else {
+ return mrb_bool(result) ? EXIT_SUCCESS : EXIT_FAILURE;
}
- return EXIT_SUCCESS;
}
-static void
-t_printstr(mrb_state *mrb, mrb_value obj)
+/* Implementation of print due to the reason that there might be no print */
+static mrb_value
+t_print(mrb_state *mrb, mrb_value self)
+{
+ const mrb_value *argv;
+ mrb_int argc;
+ mrb_int i;
+
+ mrb_get_args(mrb, "*!", &argv, &argc);
+ for (i = 0; i < argc; ++i) {
+ mrb_value s = mrb_obj_as_string(mrb, argv[i]);
+ fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stdout);
+ }
+ fflush(stdout);
+
+ return mrb_nil_value();
+}
+
+#define UNESCAPE(p, endp) ((p) != (endp) && *(p) == '\\' ? (p)+1 : (p))
+#define CHAR_CMP(c1, c2) ((unsigned char)(c1) - (unsigned char)(c2))
+
+static const char *
+str_match_bracket(const char *p, const char *pat_end,
+ const char *s, const char *str_end)
+{
+ mrb_bool ok = FALSE, negated = FALSE;
+
+ if (p == pat_end) return NULL;
+ if (*p == '!' || *p == '^') {
+ negated = TRUE;
+ ++p;
+ }
+
+ while (*p != ']') {
+ const char *t1 = p;
+ if ((t1 = UNESCAPE(t1, pat_end)) == pat_end) return NULL;
+ if ((p = t1 + 1) == pat_end) return NULL;
+ if (p[0] == '-' && p[1] != ']') {
+ const char *t2 = p + 1;
+ if ((t2 = UNESCAPE(t2, pat_end)) == pat_end) return NULL;
+ p = t2 + 1;
+ if (!ok && CHAR_CMP(*t1, *s) <= 0 && CHAR_CMP(*s, *t2) <= 0) ok = TRUE;
+ }
+ else {
+ if (!ok && CHAR_CMP(*t1, *s) == 0) ok = TRUE;
+ }
+ }
+
+ return ok == negated ? NULL : p + 1;
+}
+
+static mrb_bool
+str_match_no_brace_p(const char *pat, mrb_int pat_len,
+ const char *str, mrb_int str_len)
{
- 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);
+ const char *p = pat, *s = str;
+ const char *pat_end = pat + pat_len, *str_end = str + str_len;
+ const char *p_tmp = NULL, *s_tmp = NULL;
+
+ for (;;) {
+ if (p == pat_end) return s == str_end;
+ switch (*p) {
+ case '*':
+ do { ++p; } while (p != pat_end && *p == '*');
+ if (UNESCAPE(p, pat_end) == pat_end) return TRUE;
+ if (s == str_end) return FALSE;
+ p_tmp = p;
+ s_tmp = s;
+ continue;
+ case '?':
+ if (s == str_end) return FALSE;
+ ++p;
+ ++s;
+ continue;
+ case '[': {
+ const char *t;
+ if (s == str_end) return FALSE;
+ if ((t = str_match_bracket(p+1, pat_end, s, str_end))) {
+ p = t;
+ ++s;
+ continue;
+ }
+ goto L_failed;
+ }
+ }
+
+ /* ordinary */
+ p = UNESCAPE(p, pat_end);
+ if (s == str_end) return p == pat_end;
+ if (p == pat_end) goto L_failed;
+ if (*p++ != *s++) goto L_failed;
+ continue;
+
+ L_failed:
+ if (p_tmp && s_tmp) {
+ /* try next '*' position */
+ p = p_tmp;
+ s = ++s_tmp;
+ continue;
+ }
+
+ return FALSE;
}
}
-mrb_value
-mrb_t_printstr(mrb_state *mrb, mrb_value self)
+#define COPY_AND_INC(dst, src, len) \
+ do { memcpy(dst, src, len); dst += len; } while (0)
+
+static mrb_bool
+str_match_p(mrb_state *mrb,
+ const char *pat, mrb_int pat_len,
+ const char *str, mrb_int str_len)
{
- mrb_value argv;
+ const char *p = pat, *pat_end = pat + pat_len;
+ const char *lbrace = NULL, *rbrace = NULL;
+ int nest = 0;
+ mrb_bool ret = FALSE;
+
+ for (; p != pat_end; ++p) {
+ if (*p == '{' && nest++ == 0) lbrace = p;
+ else if (*p == '}' && lbrace && --nest == 0) { rbrace = p; break; }
+ else if (*p == '\\' && ++p == pat_end) break;
+ }
+
+ if (lbrace && rbrace) {
+ /* expand brace */
+ char *ex_pat = (char *)mrb_malloc(mrb, pat_len-2); /* expanded pattern */
+ char *ex_p = ex_pat;
+
+ COPY_AND_INC(ex_p, pat, lbrace-pat);
+ p = lbrace;
+ while (p < rbrace) {
+ char *orig_ex_p = ex_p;
+ const char *t = ++p;
+ for (nest = 0; p < rbrace && !(*p == ',' && nest == 0); ++p) {
+ if (*p == '{') ++nest;
+ else if (*p == '}') --nest;
+ else if (*p == '\\' && ++p == rbrace) break;
+ }
+ COPY_AND_INC(ex_p, t, p-t);
+ COPY_AND_INC(ex_p, rbrace+1, pat_end-rbrace-1);
+ if ((ret = str_match_p(mrb, ex_pat, ex_p-ex_pat, str, str_len))) break;
+ ex_p = orig_ex_p;
+ }
+ mrb_free(mrb, ex_pat);
+ }
+ else if (!lbrace && !rbrace) {
+ ret = str_match_no_brace_p(pat, pat_len, str, str_len);
+ }
- mrb_get_args(mrb, "o", &argv);
- t_printstr(mrb, argv);
+ return ret;
+}
- return argv;
+static mrb_value
+m_str_match_p(mrb_state *mrb, mrb_value self)
+{
+ const char *pat, *str;
+ mrb_int pat_len, str_len;
+
+ mrb_get_args(mrb, "ss", &pat, &pat_len, &str, &str_len);
+ return mrb_bool_value(str_match_p(mrb, pat, pat_len, str, str_len));
}
void
@@ -87,22 +215,25 @@ 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));
+ mrb_define_method(mrb, krn, "t_print", t_print, MRB_ARGS_ANY());
+ mrb_define_method(mrb, krn, "_str_match?", m_str_match_p, MRB_ARGS_REQ(2));
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));
+ mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_int_value(mrb, MRB_INT_MAX));
+ mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_int_value(mrb, MRB_INT_MIN));
+ mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_int_value(mrb, MRB_INT_BIT));
-#ifndef MRB_WITHOUT_FLOAT
-#ifdef MRB_USE_FLOAT
- mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6));
+#ifndef MRB_NO_FLOAT
+#ifdef MRB_USE_FLOAT32
+ mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-5));
#else
- mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12));
+ mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-10));
#endif
#endif
+ mrb_init_test_vformat(mrb);
+
if (verbose) {
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
}
@@ -121,15 +252,17 @@ mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
#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)) { \
+ if (mrb_integer_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))); \
+ mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_int_value(mrb_dst, mrb_integer(res_dst) + mrb_integer(res_src))); \
} \
} while (FALSE) \
TEST_COUNT_PASS(ok_test);
TEST_COUNT_PASS(ko_test);
TEST_COUNT_PASS(kill_test);
+ TEST_COUNT_PASS(warning_test);
+ TEST_COUNT_PASS(skip_test);
#undef TEST_COUNT_PASS
@@ -167,7 +300,8 @@ main(int argc, char **argv)
}
mrb_init_test_driver(mrb, verbose);
- mrb_init_mrbtest(mrb);
+ mrb_load_irep(mrb, mrbtest_assert_irep);
+ mrbgemtest_init(mrb);
ret = eval_test(mrb);
mrb_close(mrb);
diff --git a/mrbgems/mruby-test/init_mrbtest.c b/mrbgems/mruby-test/init_mrbtest.c
deleted file mode 100644
index 7678a5200..000000000
--- a/mrbgems/mruby-test/init_mrbtest.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <stdlib.h>
-#include <mruby.h>
-#include <mruby/irep.h>
-#include <mruby/variable.h>
-
-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);
-}
-
diff --git a/mrbgems/mruby-test/mrbgem.rake b/mrbgems/mruby-test/mrbgem.rake
index 589fc688a..927447b4f 100644
--- a/mrbgems/mruby-test/mrbgem.rake
+++ b/mrbgems/mruby-test/mrbgem.rake
@@ -3,37 +3,24 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
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")
-
- libmruby = libfile("#{build.build_dir}/lib/libmruby")
- libmruby_core = libfile("#{build.build_dir}/lib/libmruby_core")
-
- 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
+ mrbtest_lib = libfile("#{build_dir}/mrbtest")
+ mrbtest_objs = [assert_lib]
+ driver_objs = srcs_to_objs(".")
file assert_lib => assert_c
file assert_c => [assert_rb, build.mrbcfile] do |t|
+ _pp "GEN", t.name.relative_path
+ mkdir_p File.dirname(t.name)
open(t.name, 'w') do |f|
- mrbc.run f, assert_rb, 'mrbtest_assert_irep'
+ mrbc.run f, assert_rb, 'mrbtest_assert_irep', cdump: false
end
end
@@ -41,12 +28,14 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
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)
+ mrbtest_objs.concat(g.test_objs)
+ mrbtest_objs << test_rbobj
+ dep_list = build.gems.tsort_dependencies([g.name], 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)
+ _pp "GEN", t.name.relative_path
+ 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|
@@ -63,12 +52,12 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
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"
+ g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload", cdump: false
end
g.test_rbfiles.flatten.each_with_index do |rbfile, i|
- g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}"
+ g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}", cdump: false, static: true
end
- f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty?
+ f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] if g.custom_test_init?
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);]
@@ -86,7 +75,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
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[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __func__);]
f.puts %Q[ exit(EXIT_FAILURE);]
f.puts %Q[ }]
dep_list.each do |d|
@@ -131,49 +120,20 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
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, libmruby_core, libmruby] 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
+ file exec => [*driver_objs, mlib, mrbtest_lib, build.libmruby_static] do |t|
+ build.linker.run t.name, t.prerequisites, *build.gems.linker_attrs
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] do |_t|
- _pp "GEN", "*.rb", "#{clib.relative_path}"
- FileUtils.mkdir_p File.dirname(clib)
+ file clib => ["#{build.build_dir}/mrbgems/active_gems.txt", build.mrbcfile, __FILE__] do |_t|
+ _pp "GEN", clib.relative_path
+ mkdir_p File.dirname(clib)
open(clib, 'w') do |f|
f.puts %Q[/*]
f.puts %Q[ * This file contains a list of all]
@@ -184,7 +144,8 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
f.puts %Q[]
- f.puts IO.read(init)
+ f.puts %Q[struct mrb_state;]
+ f.puts %Q[typedef struct mrb_state mrb_state;]
build.gems.each do |g|
f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
end
diff --git a/mrbgems/mruby-test/vformat.c b/mrbgems/mruby-test/vformat.c
new file mode 100644
index 000000000..571d96b19
--- /dev/null
+++ b/mrbgems/mruby-test/vformat.c
@@ -0,0 +1,179 @@
+#include <string.h>
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/string.h>
+
+/* no argument */
+static mrb_value
+vf_s_format_0(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str;
+ const char *fmt;
+
+ mrb_get_args(mrb, "S", &fmt_str);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+
+ return mrb_format(mrb, fmt);
+}
+
+/* c char */
+static mrb_value
+vf_s_format_c(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str, arg_str;
+ const char *fmt;
+ char c;
+
+ mrb_get_args(mrb, "SS", &fmt_str, &arg_str);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+ c = RSTRING_CSTR(mrb, arg_str)[0];
+
+ return mrb_format(mrb, fmt, c);
+}
+
+/* d int */
+static mrb_value
+vf_s_format_d(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str;
+ const char *fmt;
+ mrb_int i;
+ int d;
+
+ mrb_get_args(mrb, "Si", &fmt_str, &i);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+ d = (int)i;
+
+ return mrb_format(mrb, fmt, d);
+}
+
+#ifndef MRB_NO_FLOAT
+/* f float */
+static mrb_value
+vf_s_format_f(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str;
+ const char *fmt;
+ mrb_float f;
+
+ mrb_get_args(mrb, "Sf", &fmt_str, &f);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+
+ return mrb_format(mrb, fmt, f);
+}
+#endif
+
+/* i fixnum */
+static mrb_value
+vf_s_format_i(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str;
+ const char *fmt;
+ mrb_int i;
+
+ mrb_get_args(mrb, "Si", &fmt_str, &i);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+
+ return mrb_format(mrb, fmt, i);
+}
+
+/* l char*, size_t */
+static mrb_value
+vf_s_format_l(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str, arg_str;
+ const char *fmt;
+ const char *s;
+ mrb_int i;
+ size_t len;
+
+ mrb_get_args(mrb, "SSi", &fmt_str, &arg_str, &i);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+ s = RSTRING_PTR(arg_str);
+ len = (size_t)i;
+ if (len > (size_t)RSTRING_LEN(arg_str)) len = (size_t)RSTRING_LEN(arg_str);
+
+ return mrb_format(mrb, fmt, s, len);
+}
+
+/* n symbol */
+static mrb_value
+vf_s_format_n(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str;
+ const char *fmt;
+ mrb_sym sym;
+
+ mrb_get_args(mrb, "Sn", &fmt_str, &sym);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+
+ return mrb_format(mrb, fmt, sym);
+}
+
+/* s char* */
+static mrb_value
+vf_s_format_s(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str, arg_str;
+ const char *fmt;
+ const char *s;
+
+ mrb_get_args(mrb, "SS", &fmt_str, &arg_str);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+ s = RSTRING_CSTR(mrb, arg_str);
+
+ return mrb_format(mrb, fmt, s);
+}
+
+/* C RClass */
+static mrb_value
+vf_s_format_C(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str, arg_cls;
+ const char *fmt;
+ struct RClass *c;
+
+ mrb_get_args(mrb, "SC", &fmt_str, &arg_cls);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+ c = mrb_class_ptr(arg_cls);
+
+ return mrb_format(mrb, fmt, c);
+}
+
+/* v value */
+static mrb_value
+vf_s_format_v(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str, arg_v;
+ const char *fmt;
+
+ mrb_get_args(mrb, "So", &fmt_str, &arg_v);
+ fmt = RSTRING_CSTR(mrb, fmt_str);
+
+ return mrb_format(mrb, fmt, arg_v);
+}
+
+void
+mrb_init_test_vformat(mrb_state *mrb)
+{
+ struct RClass *vf;
+
+ vf = mrb_define_module(mrb, "TestVFormat");
+ mrb_define_class_method(mrb, vf, "z", vf_s_format_0, MRB_ARGS_REQ(1));
+
+#define VF_DEFINE_FORMAT_METHOD(t) VF_DEFINE_FORMAT_METHOD_n(t,2)
+#define VF_DEFINE_FORMAT_METHOD_n(t,n) mrb_define_class_method(mrb, vf, #t, vf_s_format_##t, MRB_ARGS_REQ(n));
+
+ VF_DEFINE_FORMAT_METHOD(c);
+ VF_DEFINE_FORMAT_METHOD(d);
+#ifndef MRB_NO_FLOAT
+ VF_DEFINE_FORMAT_METHOD(f);
+#endif
+ VF_DEFINE_FORMAT_METHOD(i);
+ VF_DEFINE_FORMAT_METHOD_n(l,3);
+ VF_DEFINE_FORMAT_METHOD(n);
+ VF_DEFINE_FORMAT_METHOD(s);
+ VF_DEFINE_FORMAT_METHOD(C);
+ VF_DEFINE_FORMAT_METHOD(v);
+}
diff --git a/mrbgems/mruby-time/include/mruby/time.h b/mrbgems/mruby-time/include/mruby/time.h
new file mode 100644
index 000000000..1adcfd49c
--- /dev/null
+++ b/mrbgems/mruby-time/include/mruby/time.h
@@ -0,0 +1,26 @@
+/*
+** mruby/time.h - Time class
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_TIME_H
+#define MRUBY_TIME_H
+
+#include "mruby/common.h"
+#include <time.h>
+
+MRB_BEGIN_DECL
+
+typedef enum mrb_timezone {
+ MRB_TIMEZONE_NONE = 0,
+ MRB_TIMEZONE_UTC = 1,
+ MRB_TIMEZONE_LOCAL = 2,
+ MRB_TIMEZONE_LAST = 3
+} mrb_timezone;
+
+MRB_API mrb_value mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, mrb_timezone timezone);
+
+MRB_END_DECL
+
+#endif /* MRUBY_TIME_H */
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index cfd51ac63..299d4d7e1 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -4,19 +4,29 @@
** See Copyright Notice in mruby.h
*/
+#ifndef MRB_NO_FLOAT
#include <math.h>
-#include <time.h>
+#endif
+
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/data.h>
+#include <mruby/numeric.h>
+#include <mruby/time.h>
+#include <mruby/string.h>
+#include <mruby/presym.h>
-#ifndef MRB_DISABLE_STDIO
-#include <stdio.h>
-#else
+#ifdef MRB_NO_STDIO
#include <string.h>
#endif
+#include <stdlib.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
+#define TO_S_FMT "%Y-%m-%d %H:%M:%S "
#if defined(_MSC_VER) && _MSC_VER < 1800
double round(double x) {
@@ -24,8 +34,10 @@ double round(double x) {
}
#endif
-#if !defined(__MINGW64__) && defined(_WIN32)
-# define llround(x) round(x)
+#ifndef MRB_NO_FLOAT
+# if !defined(__MINGW64__) && defined(_WIN32)
+# define llround(x) round(x)
+# endif
#endif
#if defined(__MINGW64__) || defined(__MINGW32__)
@@ -46,7 +58,7 @@ double round(double x) {
/* #define NO_GMTIME_R */
#ifdef _WIN32
-#if _MSC_VER
+#ifdef _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)
@@ -54,10 +66,14 @@ double round(double x) {
#define NO_GMTIME_R
#endif
#endif
+#ifdef __STRICT_ANSI__
+/* Strict ANSI (e.g. -std=c99) do not provide gmtime_r/localtime_r */
+#define NO_GMTIME_R
+#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(). */
+/* except when MRB_NO_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 */
@@ -67,15 +83,14 @@ double round(double x) {
/* 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
+#if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(CLOCK_REALTIME)
+# define USE_CLOCK_GETTIME
+#endif
+
+#if !defined(NO_GETTIMEOFDAY)
+# if defined(_WIN32) && !defined(USE_CLOCK_GETTIME)
# define WIN32_LEAN_AND_MEAN /* don't include winsock.h */
# include <windows.h>
# define gettimeofday my_gettimeofday
@@ -166,13 +181,6 @@ timegm(struct tm *tm)
* 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;
@@ -184,7 +192,7 @@ static const mrb_timezone_name timezone_names[] = {
{ "LOCAL", sizeof("LOCAL") - 1 },
};
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
static const char mon_names[12][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};
@@ -203,21 +211,110 @@ struct mrb_time {
static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
+#ifndef MRB_NO_FLOAT
+void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
+typedef mrb_float mrb_sec;
+#define mrb_sec_value(mrb, sec) mrb_float_value(mrb, sec)
+#else
+typedef mrb_int mrb_sec;
+#define mrb_sec_value(mrb, sec) mrb_int_value(mrb, sec)
+#endif
+
+#define MRB_TIME_T_UINT (~(time_t)0 > 0)
+#define MRB_TIME_MIN ( \
+ MRB_TIME_T_UINT ? 0 : \
+ (sizeof(time_t) <= 4 ? INT32_MIN : INT64_MIN) \
+)
+#define MRB_TIME_MAX (time_t)( \
+ MRB_TIME_T_UINT ? (sizeof(time_t) <= 4 ? UINT32_MAX : UINT64_MAX) : \
+ (sizeof(time_t) <= 4 ? INT32_MAX : INT64_MAX) \
+)
+
+#ifndef MRB_NO_FLOAT
+/* return true if time_t is fit in mrb_int */
+static mrb_bool
+fixable_time_t_p(time_t v)
+{
+ if (MRB_INT_MIN <= MRB_TIME_MIN && MRB_TIME_MAX <= MRB_INT_MAX) return TRUE;
+ if (v > (time_t)MRB_INT_MAX) return FALSE;
+ if (MRB_TIME_T_UINT) return TRUE;
+ if (MRB_INT_MIN > (mrb_int)v) return FALSE;
+ return TRUE;
+}
+#endif
+
+static time_t
+mrb_to_time_t(mrb_state *mrb, mrb_value obj, time_t *usec)
+{
+ time_t t;
+
+ switch (mrb_type(obj)) {
+#ifndef MRB_NO_FLOAT
+ case MRB_TT_FLOAT:
+ {
+ mrb_float f = mrb_float(obj);
+
+ mrb_check_num_exact(mrb, f);
+ if (f >= ((mrb_float)MRB_TIME_MAX-1.0) || f < ((mrb_float)MRB_TIME_MIN+1.0)) {
+ goto out_of_range;
+ }
+
+ if (usec) {
+ t = (time_t)f;
+ *usec = (time_t)llround((f - t) * 1.0e+6);
+ }
+ else {
+ t = (time_t)llround(f);
+ }
+ }
+ break;
+#endif /* MRB_NO_FLOAT */
+ default:
+ case MRB_TT_INTEGER:
+ {
+ mrb_int i = mrb_integer(obj);
+
+ if ((MRB_INT_MAX > MRB_TIME_MAX && i > 0 && (time_t)i > MRB_TIME_MAX) ||
+ (0 > MRB_TIME_MIN && MRB_TIME_MIN > MRB_INT_MIN && MRB_TIME_MIN > i)) {
+ goto out_of_range;
+ }
+
+ t = (time_t)i;
+ if (usec) { *usec = 0; }
+ }
+ break;
+ }
+
+ return t;
+
+out_of_range:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", obj);
+
+ /* not reached */
+ if (usec) { *usec = 0; }
+ return 0;
+}
+
/** Updates the datetime of a mrb_time based on it's timezone and
-seconds setting. Returns self on success, NULL of failure. */
+ seconds setting. Returns self on success, NULL of failure.
+ if `dealloc` is set `true`, it frees `self` on error. */
static struct mrb_time*
-time_update_datetime(mrb_state *mrb, struct mrb_time *self)
+time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc)
{
struct tm *aid;
+ time_t t = self->sec;
if (self->timezone == MRB_TIMEZONE_UTC) {
- aid = gmtime_r(&self->sec, &self->datetime);
+ aid = gmtime_r(&t, &self->datetime);
}
else {
- aid = localtime_r(&self->sec, &self->datetime);
+ aid = localtime_r(&t, &self->datetime);
}
if (!aid) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec));
+ mrb_sec sec = (mrb_sec)t;
+
+ if (dealloc) mrb_free(mrb, self);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", mrb_sec_value(mrb, sec));
/* not reached */
return NULL;
}
@@ -234,58 +331,50 @@ 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)
+time_alloc_time(mrb_state *mrb, time_t sec, time_t 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->sec = sec;
+ tm->usec = usec;
+ if (MRB_TIME_T_UINT && tm->usec < 0) {
+ long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */
tm->usec -= sec2 * 1000000;
tm->sec += sec2;
}
else if (tm->usec >= 1000000) {
- long sec2 = (long)(usec / 1000000);
+ long sec2 = (long)(tm->usec / 1000000);
tm->usec -= sec2 * 1000000;
tm->sec += sec2;
}
tm->timezone = timezone;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, TRUE);
return tm;
}
+static struct mrb_time*
+time_alloc(mrb_state *mrb, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
+{
+ time_t tsec, tusec;
+
+ tsec = mrb_to_time_t(mrb, sec, &tusec);
+ tusec += mrb_to_time_t(mrb, usec, NULL);
+
+ return time_alloc_time(mrb, tsec, tusec, timezone);
+}
+
static mrb_value
-mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone)
+mrb_time_make_time(mrb_state *mrb, struct RClass *c, time_t sec, time_t usec, enum mrb_timezone timezone)
+{
+ return mrb_time_wrap(mrb, c, time_alloc_time(mrb, sec, usec, timezone));
+}
+
+static mrb_value
+mrb_time_make(mrb_state *mrb, struct RClass *c, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
{
return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone));
}
@@ -293,45 +382,53 @@ mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mr
static struct mrb_time*
current_mrb_time(mrb_state *mrb)
{
+ struct mrb_time tmzero = {0};
struct mrb_time *tm;
+ time_t sec, usec;
- tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
-#if defined(TIME_UTC)
+#if defined(TIME_UTC) && !defined(__ANDROID__)
{
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;
+ timespec_get(&ts, TIME_UTC);
+ sec = ts.tv_sec;
+ usec = ts.tv_nsec / 1000;
+ }
+#elif defined(USE_CLOCK_GETTIME)
+ {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ sec = ts.tv_sec;
+ 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;
+ sec = time(NULL);
+ if (sec != last_sec) {
+ last_sec = sec;
last_usec = 0;
}
else {
/* add 1 usec to differentiate two times */
last_usec += 1;
}
- tm->usec = last_usec;
+ usec = last_usec;
}
#else
{
struct timeval tv;
gettimeofday(&tv, NULL);
- tm->sec = tv.tv_sec;
- tm->usec = tv.tv_usec;
+ sec = tv.tv_sec;
+ usec = tv.tv_usec;
}
#endif
+ tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
+ *tm = tmzero;
+ tm->sec = sec; tm->usec = usec;
tm->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, TRUE);
return tm;
}
@@ -343,15 +440,23 @@ mrb_time_now(mrb_state *mrb, mrb_value self)
return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb));
}
+MRB_API mrb_value
+mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone zone)
+{
+ return mrb_time_make_time(mrb, mrb_class_get_id(mrb, MRB_SYM(Time)), sec, usec, zone);
+}
+
/* 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_time_at_m(mrb_state *mrb, mrb_value self)
{
- mrb_float f, f2 = 0;
+ mrb_value sec;
+ mrb_value usec = mrb_fixnum_value(0);
+
+ mrb_get_args(mrb, "o|o", &sec, &usec);
- mrb_get_args(mrb, "f|f", &f, &f2);
- return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL);
+ return mrb_time_make(mrb, mrb_class_ptr(self), sec, usec, MRB_TIMEZONE_LOCAL);
}
static struct mrb_time*
@@ -362,22 +467,29 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
time_t nowsecs;
struct tm nowtime = { 0 };
- nowtime.tm_year = (int)ayear - 1900;
- nowtime.tm_mon = (int)amonth - 1;
+#if MRB_INT_MAX > INT_MAX
+#define OUTINT(x) (((MRB_TIME_T_UINT ? 0 : INT_MIN) > (x)) || (x) > INT_MAX)
+#else
+#define OUTINT(x) 0
+#endif
+
+ if (OUTINT(ayear-1900) ||
+ amonth < 1 || amonth > 12 ||
+ aday < 1 || aday > 31 ||
+ ahour < 0 || ahour > 24 ||
+ (ahour == 24 && (amin > 0 || asec > 0)) ||
+ amin < 0 || amin > 59 ||
+ asec < 0 || asec > 60)
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "argument out of range");
+
+ 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);
}
@@ -385,10 +497,10 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
nowsecs = mktime(&nowtime);
}
if (nowsecs == (time_t)-1) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time");
}
- return time_alloc(mrb, (double)nowsecs, (double)ausec, timezone);
+ return time_alloc_time(mrb, nowsecs, ausec, timezone);
}
/* 15.2.19.6.2 */
@@ -433,11 +545,10 @@ time_get_ptr(mrb_state *mrb, mrb_value time)
static mrb_value
mrb_time_eq(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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;
@@ -448,10 +559,9 @@ mrb_time_eq(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_time_cmp(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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();
@@ -471,35 +581,83 @@ mrb_time_cmp(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(0);
}
+static mrb_noreturn void
+int_overflow(mrb_state *mrb, const char *reason)
+{
+ mrb_raisef(mrb, E_RANGE_ERROR, "time_t overflow in Time %s", reason);
+}
+
static mrb_value
mrb_time_plus(mrb_state *mrb, mrb_value self)
{
- mrb_float f;
+ mrb_value o = mrb_get_arg1(mrb);
struct mrb_time *tm;
+ time_t sec, usec;
- 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);
+ sec = mrb_to_time_t(mrb, o, &usec);
+#ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
+ if (__builtin_add_overflow(tm->sec, sec, &sec)) {
+ int_overflow(mrb, "addition");
+ }
+#else
+ if (sec >= 0) {
+ if (tm->sec > MRB_TIME_MAX - sec) {
+ int_overflow(mrb, "addition");
+ }
+ }
+ else {
+ if (tm->sec < MRB_TIME_MIN - sec) {
+ int_overflow(mrb, "addition");
+ }
+ }
+ sec = tm->sec + sec;
+#endif
+ return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), sec, tm->usec+usec, tm->timezone);
}
static mrb_value
mrb_time_minus(mrb_state *mrb, mrb_value self)
{
- mrb_float f;
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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;
+#ifndef MRB_NO_FLOAT
+ mrb_float f;
+ f = (mrb_sec)(tm->sec - tm2->sec)
+ + (mrb_sec)(tm->usec - tm2->usec) / 1.0e6;
return mrb_float_value(mrb, f);
+#else
+ mrb_int f;
+ f = tm->sec - tm2->sec;
+ if (tm->usec < tm2->usec) f--;
+ return mrb_int_value(mrb, f);
+#endif
}
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);
+ time_t sec, usec;
+ sec = mrb_to_time_t(mrb, other, &usec);
+#ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
+ if (__builtin_sub_overflow(tm->sec, sec, &sec)) {
+ int_overflow(mrb, "subtraction");
+ }
+#else
+ if (sec >= 0) {
+ if (tm->sec < MRB_TIME_MIN + sec) {
+ int_overflow(mrb, "subtraction");
+ }
+ }
+ else {
+ if (tm->sec > MRB_TIME_MAX + sec) {
+ int_overflow(mrb, "subtraction");
+ }
+ }
+ sec = tm->sec - sec;
+#endif
+ return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), sec, tm->usec-usec, tm->timezone);
}
}
@@ -560,22 +718,20 @@ mrb_time_asctime(mrb_state *mrb, mrb_value self)
struct tm *d = &tm->datetime;
int len;
-#if defined(MRB_DISABLE_STDIO)
- char *s;
+#if defined(MRB_NO_STDIO)
# ifdef NO_ASCTIME_R
- s = asctime(d);
+ char *buf = asctime(d);
# else
- char buf[32];
+ char buf[32], *s;
s = asctime_r(d, buf);
# endif
- len = strlen(s)-1; /* truncate the last newline */
+ len = strlen(buf)-1; /* truncate the last newline */
#else
- char buf[256];
+ char buf[32];
- len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d",
+ len = snprintf(buf, sizeof(buf), "%s %s %2d %02d:%02d:%02d %.4d",
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);
@@ -616,7 +772,7 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self)
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_UTC;
- time_update_datetime(mrb, tm2);
+ time_update_datetime(mrb, tm2, TRUE);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -631,7 +787,7 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self)
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm2);
+ time_update_datetime(mrb, tm2, TRUE);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -679,10 +835,9 @@ mrb_time_initialize(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy)
{
- mrb_value src;
+ mrb_value src = mrb_get_arg1(mrb);
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");
@@ -709,7 +864,7 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, FALSE);
return self;
}
@@ -757,7 +912,7 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(tm->datetime.tm_sec);
}
-
+#ifndef MRB_NO_FLOAT
/* 15.2.19.7.24 */
/* Returns a Float with the time since the epoch in seconds. */
static mrb_value
@@ -768,32 +923,32 @@ mrb_time_to_f(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
}
+#endif
/* 15.2.19.7.25 */
-/* Returns a Fixnum with the time since the epoch in seconds. */
+/* Returns an Integer 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) {
+#ifndef MRB_NO_FLOAT
+ if (!fixable_time_t_p(tm->sec)) {
return mrb_float_value(mrb, (mrb_float)tm->sec);
}
- return mrb_fixnum_value((mrb_int)tm->sec);
+#endif
+ return mrb_int_value(mrb, (mrb_int)tm->sec);
}
/* 15.2.19.7.26 */
-/* Returns a Float with the time since the epoch in microseconds. */
+/* Returns an Integer 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);
}
@@ -806,7 +961,7 @@ mrb_time_utc(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_UTC;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, FALSE);
return self;
}
@@ -821,6 +976,48 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self)
return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
}
+static size_t
+time_to_s_utc(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len)
+{
+ return strftime(buf, buf_len, TO_S_FMT "UTC", &tm->datetime);
+}
+
+static size_t
+time_to_s_local(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len)
+{
+#if defined(_MSC_VER) && _MSC_VER < 1900 || defined(__MINGW64__) || defined(__MINGW32__)
+ struct tm datetime = {0};
+ time_t utc_sec = timegm(&tm->datetime);
+ size_t len;
+ int offset;
+
+ if (utc_sec == (time_t)-1) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time");
+ }
+ offset = abs((int)(utc_sec - tm->sec) / 60);
+ datetime.tm_year = 100;
+ datetime.tm_hour = offset / 60;
+ datetime.tm_min = offset % 60;
+ len = strftime(buf, buf_len, TO_S_FMT, &tm->datetime);
+ buf[len++] = utc_sec < tm->sec ? '-' : '+';
+
+ return len + strftime(buf + len, buf_len - len, "%H%M", &datetime);
+#else
+ return strftime(buf, buf_len, TO_S_FMT "%z", &tm->datetime);
+#endif
+}
+
+static mrb_value
+mrb_time_to_s(mrb_state *mrb, mrb_value self)
+{
+ char buf[64];
+ struct mrb_time *tm = time_get_ptr(mrb, self);
+ mrb_bool utc = tm->timezone == MRB_TIMEZONE_UTC;
+ size_t len = (utc ? time_to_s_utc : time_to_s_local)(mrb, tm, buf, sizeof(buf));
+ mrb_value str = mrb_str_new(mrb, buf, len);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
+}
void
mrb_mruby_time_gem_init(mrb_state* mrb)
@@ -830,7 +1027,7 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
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, "at", mrb_time_at_m, 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 */
@@ -841,8 +1038,8 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
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, "to_s" , mrb_time_to_s , MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "inspect", mrb_time_to_s , 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 */
@@ -862,7 +1059,9 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
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 */
+#ifndef MRB_NO_FLOAT
mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */
+#endif
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 */
diff --git a/mrbgems/mruby-time/test/time.rb b/mrbgems/mruby-time/test/time.rb
index 54c446ca3..e239dec28 100644
--- a/mrbgems/mruby-time/test/time.rb
+++ b/mrbgems/mruby-time/test/time.rb
@@ -2,16 +2,17 @@
# Time ISO Test
assert('Time.new', '15.2.3.3.3') do
- Time.new.class == Time
+ assert_equal(Time, Time.new.class)
end
assert('Time', '15.2.19') do
- Time.class == Class
+ assert_equal(Class, Time.class)
end
assert('Time.at', '15.2.19.6.1') do
assert_kind_of(Time, Time.at(1300000000.0))
+ skip unless Object.const_defined?(:Float)
assert_raise(FloatDomainError) { Time.at(Float::NAN) }
assert_raise(FloatDomainError) { Time.at(Float::INFINITY) }
assert_raise(FloatDomainError) { Time.at(-Float::INFINITY) }
@@ -21,76 +22,106 @@ assert('Time.at', '15.2.19.6.1') do
end
assert('Time.gm', '15.2.19.6.2') do
- Time.gm(2012, 12, 23)
+ t = Time.gm(2012, 9, 23)
+ assert_operator(2012, :eql?, t.year)
+ assert_operator( 9, :eql?, t.month)
+ assert_operator( 23, :eql?, t.day)
+ assert_operator( 0, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time.local', '15.2.19.6.3') do
- Time.local(2012, 12, 23)
+ t = Time.local(2014, 12, 27, 18)
+ assert_operator(2014, :eql?, t.year)
+ assert_operator( 12, :eql?, t.month)
+ assert_operator( 27, :eql?, t.day)
+ assert_operator( 18, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time.mktime', '15.2.19.6.4') do
- Time.mktime(2012, 12, 23)
+ t = Time.mktime(2013, 10, 4, 6, 15, 58, 3485)
+ assert_operator(2013, :eql?, t.year)
+ assert_operator( 10, :eql?, t.month)
+ assert_operator( 4, :eql?, t.day)
+ assert_operator( 6, :eql?, t.hour)
+ assert_operator( 15, :eql?, t.min)
+ assert_operator( 58, :eql?, t.sec)
+ assert_operator(3485, :eql?, t.usec)
end
assert('Time.now', '15.2.19.6.5') do
- Time.now.class == Time
+ assert_equal(Time, Time.now.class)
end
assert('Time.utc', '15.2.19.6.6') do
- Time.utc(2012, 12, 23)
+ t = Time.utc(2034)
+ assert_operator(2034, :eql?, t.year)
+ assert_operator( 1, :eql?, t.month)
+ assert_operator( 1, :eql?, t.day)
+ assert_operator( 0, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time#+', '15.2.19.7.1') do
- t1 = Time.at(1300000000.0)
+ t1 = Time.at(1300000000)
t2 = t1.+(60)
- assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011")
+ assert_equal("Sun Mar 13 07:07:40 2011", t2.utc.asctime)
+ skip unless Object.const_defined?(:Float)
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)
+ t1 = Time.at(1300000000)
t2 = t1.-(60)
- assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011")
+ assert_equal("Sun Mar 13 07:05:40 2011", t2.utc.asctime)
+ skip unless Object.const_defined?(:Float)
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)
+ t1 = Time.at(1300000000)
+ t2 = Time.at(1400000000)
+ t3 = Time.at(1500000000)
- t2.<=>(t1) == 1 and
- t2.<=>(t2) == 0 and
- t2.<=>(t3) == -1 and
- t2.<=>(nil) == nil
+ assert_equal(1, t2 <=> t1)
+ assert_equal(0, t2 <=> t2)
+ assert_equal(-1, t2 <=> t3)
+ assert_nil(t2 <=> 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"
+ assert_equal("Thu Mar 4 05:06:07 1982", Time.gm(1982,3,4,5,6,7).asctime)
end
assert('Time#ctime', '15.2.19.7.5') do
- Time.at(1300000000.0).utc.ctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Thu Oct 24 15:26:47 2013", Time.gm(2013,10,24,15,26,47).ctime)
end
assert('Time#day', '15.2.19.7.6') do
- Time.gm(2012, 12, 23).day == 23
+ assert_equal(23, Time.gm(2012, 12, 23).day)
end
assert('Time#dst?', '15.2.19.7.7') do
- not Time.gm(2012, 12, 23).utc.dst?
+ assert_not_predicate(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"
+ assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000).getgm.asctime)
end
assert('Time#getlocal', '15.2.19.7.9') do
@@ -98,114 +129,124 @@ assert('Time#getlocal', '15.2.19.7.9') do
t2 = Time.at(1300000000.0)
t3 = t1.getlocal
- t1 == t3 and t3 == t2.getlocal
+ assert_equal(t1, t3)
+ assert_equal(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"
+ assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000).getutc.asctime)
end
assert('Time#gmt?', '15.2.19.7.11') do
- Time.at(1300000000.0).utc.gmt?
+ assert_predicate(Time.at(1300000000).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
+ t = Time.now
+ assert_predicate(t.gmtime, :gmt?)
+ assert_predicate(t, :gmt?)
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
+ assert_equal(7, Time.gm(2012, 12, 23, 7, 6).hour)
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
+ t = Time.at(7.0e6)
+ assert_equal(t, t.clone)
end
assert('Time#localtime', '15.2.19.7.18') do
- t1 = Time.at(1300000000.0)
- t2 = Time.at(1300000000.0)
+ t1 = Time.utc(2014, 5 ,6)
+ t2 = Time.utc(2014, 5 ,6)
+ t3 = t2.getlocal
- t1.localtime
- t1 == t2.getlocal
+ assert_equal(t3, t1.localtime)
+ assert_equal(t3, t1)
end
assert('Time#mday', '15.2.19.7.19') do
- Time.gm(2012, 12, 23).mday == 23
+ assert_equal(23, Time.gm(2012, 12, 23).mday)
end
assert('Time#min', '15.2.19.7.20') do
- Time.gm(2012, 12, 23, 7, 6).min == 6
+ assert_equal(6, Time.gm(2012, 12, 23, 7, 6).min)
end
assert('Time#mon', '15.2.19.7.21') do
- Time.gm(2012, 12, 23).mon == 12
+ assert_equal(12, Time.gm(2012, 12, 23).mon)
end
assert('Time#month', '15.2.19.7.22') do
- Time.gm(2012, 12, 23).month == 12
+ assert_equal(12, Time.gm(2012, 12, 23).month)
end
assert('Times#sec', '15.2.19.7.23') do
- Time.gm(2012, 12, 23, 7, 6, 40).sec == 40
+ assert_equal(40, Time.gm(2012, 12, 23, 7, 6, 40).sec)
end
assert('Time#to_f', '15.2.19.7.24') do
- Time.at(1300000000.0).to_f == 1300000000.0
+ skip unless Object.const_defined?(:Float)
+ assert_operator(2.0, :eql?, Time.at(2).to_f)
end
assert('Time#to_i', '15.2.19.7.25') do
- Time.at(1300000000.0).to_i == 1300000000
+ assert_operator(2, :eql?, Time.at(2).to_i)
end
assert('Time#usec', '15.2.19.7.26') do
- Time.at(1300000000.0).usec == 0
+ assert_equal(0, Time.at(1300000000).usec)
+ skip unless Object.const_defined?(:Float)
+ assert_equal(0, Time.at(1300000000.0).usec)
end
assert('Time#utc', '15.2.19.7.27') do
- Time.at(1300000000.0).utc
+ t = Time.now
+ assert_predicate(t.utc, :gmt?)
+ assert_predicate(t, :gmt?)
end
assert('Time#utc?', '15.2.19.7.28') do
- Time.at(1300000000.0).utc.utc?
+ assert_predicate(Time.at(1300000000).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
+ assert_equal(0, Time.gm(2012, 12, 23).wday)
end
assert('Time#yday', '15.2.19.7.31') do
- Time.gm(2012, 12, 23).yday == 358
+ assert_equal(358, Time.gm(2012, 12, 23).yday)
end
assert('Time#year', '15.2.19.7.32') do
- Time.gm(2012, 12, 23).year == 2012
+ assert_equal(2012, Time.gm(2012, 12, 23).year)
end
assert('Time#zone', '15.2.19.7.33') do
- Time.at(1300000000.0).utc.zone == 'UTC'
+ assert_equal('UTC', Time.at(1300000000).utc.zone)
end
# Not ISO specified
assert('Time#to_s') do
- Time.at(1300000000.0).utc.to_s == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("2003-04-05 06:07:08 UTC", Time.gm(2003,4,5,6,7,8,9).to_s)
end
assert('Time#inspect') do
- Time.at(1300000000.0).utc.inspect == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_match("2013-10-28 16:27:48 [+-][0-9][0-9][0-9][0-9]",
+ Time.local(2013,10,28,16,27,48).inspect)
end
assert('day of week methods') do
@@ -224,7 +265,7 @@ assert('2000 times 500us make a second') do
2000.times do
t += 0.0005
end
- t.usec == 0
+ assert_equal(0, t.usec)
end
assert('Time.gm with Dec 31 23:59:59 1969 raise ArgumentError') do
diff --git a/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb b/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb
index 774562398..664008d1c 100644
--- a/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb
+++ b/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb
@@ -1,5 +1,5 @@
-def self.include (*modules)
+def self.include(*modules)
self.class.include(*modules)
end
diff --git a/mrbgems/mruby-toplevel-ext/test/toplevel.rb b/mrbgems/mruby-toplevel-ext/test/toplevel.rb
index 26aae9a7d..369a14909 100644
--- a/mrbgems/mruby-toplevel-ext/test/toplevel.rb
+++ b/mrbgems/mruby-toplevel-ext/test/toplevel.rb
@@ -21,4 +21,3 @@ assert('Toplevel#include') do
assert_equal :foo, method_foo
assert_equal :bar2, CONST_BAR
end
-
diff --git a/mrbgems/stdlib-ext.gembox b/mrbgems/stdlib-ext.gembox
new file mode 100644
index 000000000..2177dca36
--- /dev/null
+++ b/mrbgems/stdlib-ext.gembox
@@ -0,0 +1,18 @@
+# It also works with MRB_NO_STDIO and MRB_NO_FLOAT.
+
+MRuby::GemBox.new do |conf|
+ # Use standard Array#pack, String#unpack methods
+ conf.gem :core => "mruby-pack"
+
+ # Use standard Kernel#sprintf method
+ conf.gem :core => "mruby-sprintf"
+
+ # Use standard Time class
+ conf.gem :core => "mruby-time"
+
+ # Use standard Struct class
+ conf.gem :core => "mruby-struct"
+
+ # Use Random class
+ conf.gem :core => "mruby-random"
+end
diff --git a/mrbgems/stdlib-io.gembox b/mrbgems/stdlib-io.gembox
new file mode 100644
index 000000000..cd1c2049c
--- /dev/null
+++ b/mrbgems/stdlib-io.gembox
@@ -0,0 +1,12 @@
+# It also works with MRB_NO_FLOAT.
+
+MRuby::GemBox.new do |conf|
+ # Use standard IO/File class
+ conf.gem :core => "mruby-io"
+
+ # Use standard IO/File class
+ conf.gem :core => "mruby-socket"
+
+ # Use standard print/puts/p
+ conf.gem :core => "mruby-print"
+end
diff --git a/mrbgems/stdlib.gembox b/mrbgems/stdlib.gembox
new file mode 100644
index 000000000..72fb9e343
--- /dev/null
+++ b/mrbgems/stdlib.gembox
@@ -0,0 +1,54 @@
+# It also works with MRB_NO_STDIO and MRB_NO_FLOAT.
+
+MRuby::GemBox.new do |conf|
+ # 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 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"
+
+ # Use Kernel module extension
+ conf.gem :core => "mruby-kernel-ext"
+
+ # Use class/module extension
+ conf.gem :core => "mruby-class-ext"
+end
diff --git a/mrblib/00class.rb b/mrblib/00class.rb
index 1811236f0..d7a718ba1 100644
--- a/mrblib/00class.rb
+++ b/mrblib/00class.rb
@@ -1,5 +1,15 @@
+class BasicObject
+ def !=(other)
+ if self == other
+ false
+ else
+ true
+ end
+ end
+end
+
class Module
- # 15.2.2.4.12
+ # 15.2.2.4.12
def attr_accessor(*names)
attr_reader(*names)
attr_writer(*names)
diff --git a/mrblib/10error.rb b/mrblib/10error.rb
index 0d9f38d58..aa9da91bb 100644
--- a/mrblib/10error.rb
+++ b/mrblib/10error.rb
@@ -2,8 +2,8 @@
class ArgumentError < StandardError
end
-# ISO 15.2.25 says "LocalJumpError < StandardError"
-class LocalJumpError < ScriptError
+# ISO 15.2.25
+class LocalJumpError < StandardError
end
# ISO 15.2.26
@@ -21,6 +21,10 @@ end
class TypeError < StandardError
end
+# ISO 15.2.30
+class ZeroDivisionError < StandardError
+end
+
# ISO 15.2.31
class NameError < StandardError
attr_accessor :name
diff --git a/mrblib/array.rb b/mrblib/array.rb
index 13c5d646c..6b4f74b96 100644
--- a/mrblib/array.rb
+++ b/mrblib/array.rb
@@ -4,7 +4,6 @@
#
# ISO 15.2.12
class Array
-
##
# Calls the given block for each element of +self+
# and pass the respective element.
@@ -66,7 +65,11 @@ class Array
#
# ISO 15.2.12.5.15
def initialize(size=0, obj=nil, &block)
- raise TypeError, "expected Integer for 1st argument" unless size.kind_of? Integral
+ if size.is_a?(Array) && obj==nil && block == nil
+ self.replace(size)
+ return self
+ end
+ size = size.__to_int
raise ArgumentError, "negative array size" if size < 0
self.clear
@@ -83,20 +86,25 @@ class Array
self
end
- def _inspect
- return "[]" if self.size == 0
- "["+self.map{|x|x.inspect}.join(", ")+"]"
+ def _inspect(recur_list)
+ size = self.size
+ return "[]" if size == 0
+ return "[...]" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
+ ary=[]
+ i=0
+ while i<size
+ ary<<self[i]._inspect(recur_list)
+ i+=1
+ end
+ "["+ary.join(", ")+"]"
end
##
# Return the contents of this array as a string.
#
# ISO 15.2.12.5.31 (x)
def inspect
- begin
- self._inspect
- rescue SystemStackError
- "[...]"
- end
+ self._inspect({})
end
# ISO 15.2.12.5.32 (x)
alias to_s inspect
@@ -184,52 +192,71 @@ class Array
return block.call if ret.nil? && block
ret
end
-end
-
-##
-# Array is enumerable
-class Array
- # ISO 15.2.12.3
- include Enumerable
##
- # Quick sort
- # left : the beginning of sort region
- # right : the end of sort region
- def __sort_sub__(left, right, &block)
- stack = [ [left, right] ]
+ # Sort all elements and replace +self+ with these
+ # elements.
+ def sort!(&block)
+ stack = [ [ 0, self.size - 1 ] ]
until stack.empty?
- left, right = stack.pop
- if left < right
- i = left
- j = right
- pivot = self[i + (j - i) / 2]
- while true
- while ((block)? block.call(self[i], pivot): (self[i] <=> pivot)) < 0
- i += 1
- end
- while ((block)? block.call(pivot, self[j]): (pivot <=> self[j])) < 0
- j -= 1
+ 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]
+ cmp = if block then block.call(lval,rval) else lval <=> rval end
+ if cmp.nil?
+ raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
+ end
+ if cmp > 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
- break if (i >= j)
- tmp = self[i]; self[i] = self[j]; self[j] = tmp;
- i += 1
- j -= 1
end
- stack.push [left, i-1]
- stack.push [j+1, right]
- end
- end
- end
- # private :__sort_sub__
+ else
+ lary = self[left, mid - left]
+ lsize = lary.size
- ##
- # Sort all elements and replace +self+ with these
- # elements.
- def sort!(&block)
- size = self.size
- if size > 1
- __sort_sub__(0, size - 1, &block)
+ # 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]
+ cmp = if block then block.call(lval,rval) else lval <=> rval end
+ if cmp.nil?
+ raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
+ end
+ if cmp <= 0
+ self[i] = lval
+ lidx += 1
+ else
+ self[i] = rval
+ ridx += 1
+ end
+ end
+ }
+ end
end
self
end
@@ -237,4 +264,14 @@ class Array
def sort(&block)
self.dup.sort!(&block)
end
+
+ def to_a
+ self
+ end
+ alias entries to_a
+
+ ##
+ # Array is enumerable
+ # ISO 15.2.12.3
+ include Enumerable
end
diff --git a/mrblib/enum.rb b/mrblib/enum.rb
index a97a26f97..23b275ae2 100644
--- a/mrblib/enum.rb
+++ b/mrblib/enum.rb
@@ -10,9 +10,11 @@
# implement a meaningful `<=>` operator, as these methods
# rely on an ordering between members of the collection.
#
-# @ISO 15.3.2
+# ISO 15.3.2
module Enumerable
+ NONE = Object.new
+
##
# Call the given block for each element
# which is yield by +each+. Return false
@@ -63,22 +65,22 @@ module Enumerable
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.
+ # Return the first element for which
+ # value from the block is true. If no
+ # object matches, calls +ifnone+ and
+ # returns its result. Otherwise returns
+ # +nil+.
#
# ISO 15.3.2.2.4
def detect(ifnone=nil, &block)
- ret = ifnone
+ return to_enum :detect, ifnone unless block
+
self.each{|*val|
if block.call(*val)
- ret = val.__svalue
- break
+ return val.__svalue
end
}
- ret
+ ifnone.call unless ifnone.nil?
end
##
@@ -282,6 +284,8 @@ module Enumerable
#
# ISO 15.3.2.2.16
def partition(&block)
+ return to_enum :partition unless block
+
ary_T = []
ary_F = []
self.each{|*val|
@@ -302,6 +306,8 @@ module Enumerable
#
# ISO 15.3.2.2.17
def reject(&block)
+ return to_enum :reject unless block
+
ary = []
self.each{|*val|
ary.push(val.__svalue) unless block.call(*val)
diff --git a/mrblib/float.rb b/mrblib/float.rb
deleted file mode 100644
index 2b86dc1e5..000000000
--- a/mrblib/float.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-##
-# 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")
diff --git a/mrblib/hash.rb b/mrblib/hash.rb
index 582cfbc6c..ac70f4c70 100644
--- a/mrblib/hash.rb
+++ b/mrblib/hash.rb
@@ -4,6 +4,12 @@
# ISO 15.2.13
class Hash
##
+ # Hash is enumerable
+ #
+ # ISO 15.2.13.3
+ include Enumerable
+
+ ##
# 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
# <code>Object#==</code>) the corresponding elements in the other
@@ -12,9 +18,7 @@ class Hash
# ISO 15.2.13.4.1
def ==(hash)
return true if self.equal?(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
+ unless Hash === hash
return false
end
return false if self.size != hash.size
@@ -32,9 +36,7 @@ class Hash
# ISO 15.2.13.4.32 (x)
def eql?(hash)
return true if self.equal?(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
+ unless Hash === hash
return false
end
return false if self.size != hash.size
@@ -144,30 +146,9 @@ class Hash
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 <i>hsh</i> with the contents of other hash
- #
- # ISO 15.2.13.4.23
- def replace(hash)
- raise TypeError, "can't convert argument into Hash" unless hash.respond_to?(:to_hash)
- self.clear
- hash = hash.to_hash
- 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.values.each{|v| block.call(v)}
self
end
- # ISO 15.2.13.4.17
- alias initialize_copy replace
##
# Return a hash which contains the content of
@@ -178,8 +159,7 @@ class Hash
#
# ISO 15.2.13.4.22
def merge(other, &block)
- raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
- other = other.to_hash
+ raise TypeError, "Hash required (#{other.class} given)" unless Hash === other
h = self.dup
if block
other.each_key{|k|
@@ -192,22 +172,27 @@ class Hash
end
# internal method for Hash inspection
- def _inspect
+ def _inspect(recur_list)
return "{}" if self.size == 0
- "{"+self.map {|k,v|
- k._inspect + "=>" + v._inspect
- }.join(", ")+"}"
+ return "{...}" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
+ ary=[]
+ keys=self.keys
+ vals=self.values
+ size=keys.size
+ i=0
+ while i<size
+ ary<<(keys[i]._inspect(recur_list) + "=>" + vals[i]._inspect(recur_list))
+ 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
+ self._inspect({})
end
# ISO 15.2.13.4.31 (x)
alias to_s inspect
@@ -318,11 +303,3 @@ class Hash
h
end
end
-
-##
-# Hash is enumerable
-#
-# ISO 15.2.13.3
-class Hash
- include Enumerable
-end
diff --git a/mrblib/init_mrblib.c b/mrblib/init_mrblib.c
deleted file mode 100644
index 4d4bcd25a..000000000
--- a/mrblib/init_mrblib.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include <mruby.h>
-#include <mruby/irep.h>
-
-extern const uint8_t mrblib_irep[];
-
-void
-mrb_init_mrblib(mrb_state *mrb)
-{
- mrb_load_irep(mrb, mrblib_irep);
-}
-
diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb
index 4700684b6..7c3ea9420 100644
--- a/mrblib/kernel.rb
+++ b/mrblib/kernel.rb
@@ -40,7 +40,7 @@ module Kernel
end
# internal method for inspect
- def _inspect
+ def _inspect(_recur_list)
self.inspect
end
diff --git a/mrblib/mrblib.rake b/mrblib/mrblib.rake
deleted file mode 100644
index fe4aae1f7..000000000
--- a/mrblib/mrblib.rake
+++ /dev/null
@@ -1,21 +0,0 @@
-MRuby.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 << 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
diff --git a/mrblib/numeric.rb b/mrblib/numeric.rb
index 1b11e92ad..71f788b83 100644
--- a/mrblib/numeric.rb
+++ b/mrblib/numeric.rb
@@ -34,11 +34,11 @@ class Numeric
end
##
-# Integral
+# Integer
#
-# mruby special - module to share methods between Floats and Integers
-# to make them compatible
-module Integral
+# ISO 15.2.8
+##
+class Integer
##
# Calls the given block once for each Integer
# from +self+ downto +num+.
@@ -104,15 +104,15 @@ module Integral
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
+ i = __coerce_step_counter(num, step)
+ if num == self || step.infinite?
+ block.call(i) if step > 0 && i <= (num||i) || step < 0 && i >= (num||-i)
+ elsif num == nil
while true
block.call(i)
- i+=step
+ i += step
end
- return self
- end
- if step > 0
+ elsif step > 0
while i <= num
block.call(i)
i += step
@@ -125,19 +125,12 @@ module Integral
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
+ def ceil(n=0)
self
end
@@ -145,7 +138,7 @@ class Integer
# Returns the receiver simply.
#
# ISO 15.2.8.3.17
- def floor
+ def floor(n=0)
self
end
@@ -161,3 +154,35 @@ class Integer
# ISO 15.2.8.3.26
alias truncate floor
end
+
+class Float
+ ##
+ # 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 = self
+ if num == self || step.infinite?
+ block.call(i) if step > 0 && i <= (num||i) || step < 0 && i >= (num||-i)
+ elsif num == nil
+ while true
+ block.call(i)
+ i += step
+ end
+ elsif 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 if Object.const_defined?(:Float)
diff --git a/mrblib/range.rb b/mrblib/range.rb
index 5bd2521e8..f808053ca 100644
--- a/mrblib/range.rb
+++ b/mrblib/range.rb
@@ -3,6 +3,11 @@
#
# ISO 15.2.14
class Range
+ ##
+ # Range is enumerable
+ #
+ # ISO 15.2.14.3
+ include Enumerable
##
# Calls the given block for each element of +self+
@@ -12,10 +17,27 @@ class Range
def each(&block)
return to_enum :each unless block
- val = self.first
- last = self.last
+ val = self.begin
+ last = self.end
+
+ if val.kind_of?(Integer) && last.nil?
+ i = val
+ while true
+ block.call(i)
+ i += 1
+ end
+ return self
+ end
+
+ if val.kind_of?(String) && last.nil?
+ if val.respond_to? :__upto_endless
+ return val.__upto_endless(&block)
+ else
+ str_each = true
+ end
+ end
- if val.kind_of?(Fixnum) && last.kind_of?(Fixnum) # fixnums are special
+ if val.kind_of?(Integer) && last.kind_of?(Integer) # fixnums are special
lim = last
lim += 1 unless exclude_end?
i = val
@@ -26,7 +48,7 @@ class Range
return self
end
- if val.kind_of?(String) && last.kind_of?(String) # fixnums are special
+ if val.kind_of?(String) && last.kind_of?(String) # strings are special
if val.respond_to? :upto
return val.upto(last, exclude_end?, &block)
else
@@ -56,12 +78,20 @@ class Range
h += 1 if self.exclude_end?
h
end
-end
-##
-# Range is enumerable
-#
-# ISO 15.2.14.3
-class Range
- include Enumerable
+ ##
+ # call-seq:
+ # rng.to_a -> array
+ # rng.entries -> array
+ #
+ # Returns an array containing the items in the range.
+ #
+ # (1..7).to_a #=> [1, 2, 3, 4, 5, 6, 7]
+ # (1..).to_a #=> RangeError: cannot convert endless range to an array
+ def to_a
+ a = __num_to_a
+ return a if a
+ super
+ end
+ alias entries to_a
end
diff --git a/mrblib/string.rb b/mrblib/string.rb
index 07b80b340..675026e73 100644
--- a/mrblib/string.rb
+++ b/mrblib/string.rb
@@ -3,24 +3,42 @@
#
# ISO 15.2.10
class String
+ # ISO 15.2.10.3
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 = 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
+ def each_line(separator = "\n", &block)
+ return to_enum(:each_line, separator) unless block
+
+ if separator.nil?
+ block.call(self)
+ return self
+ end
+ raise TypeError unless separator.is_a?(String)
+
+ paragraph_mode = false
+ if separator.empty?
+ paragraph_mode = true
+ separator = "\n\n"
end
- block.call(this[offset, this.size - offset]) if this.size > offset
+ start = 0
+ string = dup
+ self_len = length
+ sep_len = separator.length
+
+ while (pointer = string.index(separator, start))
+ pointer += sep_len
+ pointer += 1 while paragraph_mode && string[pointer] == "\n"
+ block.call(string[start, pointer - start])
+ start = pointer
+ end
+ return self if start == self_len
+
+ block.call(string[start, self_len - start])
self
end
@@ -67,7 +85,7 @@ class String
block = nil
end
if !replace.nil? || !block
- replace = replace.to_str
+ replace.__to_str
end
offset = 0
result = []
@@ -103,18 +121,15 @@ class String
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
+# ##
+# # 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(pattern, &block)
+# # TODO: String#scan is not implemented yet
+# end
##
# Replace only the first match of +pattern+ with
@@ -129,12 +144,12 @@ class String
end
pattern, replace = *args
- pattern = pattern.to_str
+ pattern.__to_str
if args.length == 2 && block
block = nil
end
unless block
- replace = replace.to_str
+ replace.__to_str
end
result = []
this = dup
@@ -166,20 +181,9 @@ class String
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)
+ return to_enum(:each_byte, &block) unless block
bytes = self.bytes
pos = 0
while pos < bytes.size
@@ -189,87 +193,16 @@ class String
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
-
+ # those two methods requires Regexp that is optional in mruby
##
# ISO 15.2.10.5.3
- def =~(re)
- raise TypeError, "type mismatch: String given" if re.respond_to? :to_str
- re =~ self
- end
+ #def =~(re)
+ # re =~ self
+ #end
##
# ISO 15.2.10.5.27
- def match(re, &block)
- if re.respond_to? :to_str
- 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
+ #def match(re, &block)
+ # re.match(self, &block)
+ #end
end
diff --git a/mruby-source.gemspec b/mruby-source.gemspec
index 62d4c0d12..b87dda3a4 100644
--- a/mruby-source.gemspec
+++ b/mruby-source.gemspec
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
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.homepage = "https://mruby.org"
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0")
diff --git a/oss-fuzz/config/mruby.dict b/oss-fuzz/config/mruby.dict
new file mode 100644
index 000000000..a332d3505
--- /dev/null
+++ b/oss-fuzz/config/mruby.dict
@@ -0,0 +1,105 @@
+keyword___ENCODING__="__ENCODING__"
+keyword___FILE__="__FILE__"
+keyword___LINE__="__LINE__"
+keyword_BEGIN="BEGIN"
+keyword_END="END"
+keyword_alias="alias"
+keyword_and="and"
+keyword_begin="begin"
+keyword_break="break"
+keyword_case="case"
+keyword_class="class"
+keyword_def="def"
+keyword_do="do"
+keyword_else="else"
+keyword_elsif="elsif"
+keyword_end="end"
+keyword_ensure="ensure"
+keyword_false="false"
+keyword_for="for"
+keyword_if="if"
+keyword_in="in"
+keyword_module="module"
+keyword_next="next"
+keyword_nil="nil"
+keyword_not="not"
+keyword_or="or"
+keyword_redo="redo"
+keyword_rescue="rescue"
+keyword_retry="retry"
+keyword_return="return"
+keyword_self="self"
+keyword_super="super"
+keyword_then="then"
+keyword_true="true"
+keyword_undef="undef"
+keyword_unless="unless"
+keyword_until="until"
+keyword_when="when"
+keyword_while="while"
+keyword_yield="yield"
+
+operator_a=" !"
+operator_b=" ~"
+operator_c=" +"
+operator_d=" -"
+operator_e=" []"
+operator_f=" []="
+operator_g=" *"
+operator_h=" /"
+operator_i=" %"
+operator_j=" +-"
+operator_k=" >>"
+operator_l=" <<"
+operator_m=" &"
+operator_n=" ^"
+operator_o=" |"
+operator_p=" <="
+operator_q=" <>"
+operator_r=" >="
+operator_s=" <=>"
+operator_t=" =="
+operator_u=" ==="
+operator_v=" !="
+operator_w=" =~"
+operator_x=" !~"
+operator_y=" &&"
+operator_z=" ||"
+operator_aa=" .."
+operator_ab=" ..."
+operator_ac=" ?"
+operator_ad=" :"
+operator_ae=" ="
+operator_af=" %="
+operator_ag=" /="
+operator_ah=" -="
+operator_ai=" +="
+operator_aj=" |="
+operator_ak=" &="
+operator_al=" >>="
+operator_am=" <<="
+operator_an=" *="
+operator_ao=" &&="
+operator_ap=" ||="
+operator_aq=" **="
+operator_ar=" ^="
+operator_as=" not"
+operator_at=" or"
+operator_au=" and"
+operator_av=" if"
+operator_aw=" unless"
+operator_ax=" while"
+operator_ay=" until"
+operator_az=" begin"
+operator_ba=" end"
+
+snippet_1eq1=" 1=1"
+snippet_dollar=" $1"
+snippet_at=" @a"
+snippet_symbol=" :a"
+snippet_array=" [1,2]"
+snippet_block=" 1.times{|x| x}"
+snippet_multi=" 1*1"
+
+string_single_q=" 'a'"
+string_dbl_q=" \"a\""
diff --git a/oss-fuzz/config/mruby_fuzzer.options b/oss-fuzz/config/mruby_fuzzer.options
new file mode 100644
index 000000000..8658e71b2
--- /dev/null
+++ b/oss-fuzz/config/mruby_fuzzer.options
@@ -0,0 +1,5 @@
+[libfuzzer]
+close_fd_mask = 3
+dict = mruby.dict
+fork = 1
+only_ascii = 1
diff --git a/oss-fuzz/config/mruby_proto_fuzzer.options b/oss-fuzz/config/mruby_proto_fuzzer.options
new file mode 100644
index 000000000..4ced8516c
--- /dev/null
+++ b/oss-fuzz/config/mruby_proto_fuzzer.options
@@ -0,0 +1,4 @@
+[libfuzzer]
+close_fd_mask = 3
+dict = mruby.dict
+fork = 1
diff --git a/oss-fuzz/mruby_fuzzer.c b/oss-fuzz/mruby_fuzzer.c
new file mode 100644
index 000000000..9d3d44a5b
--- /dev/null
+++ b/oss-fuzz/mruby_fuzzer.c
@@ -0,0 +1,18 @@
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/compile.h>
+
+int LLVMFuzzerTestOneInput(uint8_t *Data, size_t size) {
+ if (size < 1) {
+ return 0;
+ }
+ char *code = malloc(size+1);
+ memcpy(code, Data, size);
+ code[size] = '\0';
+ mrb_state *mrb = mrb_open();
+ mrb_load_string(mrb, code);
+ mrb_close(mrb);
+ free(code);
+ return 0;
+}
diff --git a/oss-fuzz/mruby_proto_fuzzer.cpp b/oss-fuzz/mruby_proto_fuzzer.cpp
new file mode 100644
index 000000000..6dbfe3145
--- /dev/null
+++ b/oss-fuzz/mruby_proto_fuzzer.cpp
@@ -0,0 +1,44 @@
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include <mruby.h>
+#include <mruby/compile.h>
+
+#include <src/libfuzzer/libfuzzer_macro.h>
+#include <ruby.pb.h>
+#include "proto_to_ruby.h"
+
+using namespace ruby_fuzzer;
+using namespace std;
+
+int FuzzRB(const uint8_t *Data, size_t size) {
+ mrb_value v;
+ mrb_state *mrb = mrb_open();
+ if (!mrb)
+ return 0;
+
+ char *code = (char *)malloc(size+1);
+ if (!code)
+ return 0;
+ memcpy(code, Data, size);
+ code[size] = '\0';
+
+ if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
+ // With libFuzzer binary run this to generate an RB file x.rb:
+ // PROTO_FUZZER_DUMP_PATH=x.rb ./a.out proto-input
+ std::ofstream of(dump_path);
+ of.write(code, size);
+ }
+ v = mrb_load_string(mrb, code);
+ mrb_close(mrb);
+
+ free(code);
+ return 0;
+}
+
+DEFINE_PROTO_FUZZER(const Function &function) {
+ protoConverter converter;
+ auto s = converter.FunctionToString(function);
+ (void)FuzzRB((const uint8_t*)s.data(), s.size());
+}
diff --git a/oss-fuzz/proto_to_ruby.cpp b/oss-fuzz/proto_to_ruby.cpp
new file mode 100644
index 000000000..92ad039e2
--- /dev/null
+++ b/oss-fuzz/proto_to_ruby.cpp
@@ -0,0 +1,455 @@
+#include "proto_to_ruby.h"
+
+using namespace ruby_fuzzer;
+
+std::string protoConverter::removeSpecial(const std::string &x)
+{
+ std::string tmp(x);
+ if (!tmp.empty())
+ tmp.erase(std::remove_if(tmp.begin(), tmp.end(),
+ [](char c) { return !(std::isalpha(c) || std::isdigit(c)); } ), tmp.end());
+ return tmp;
+}
+
+void protoConverter::visit(ArrType const& x)
+{
+ if (x.elements_size() > 0) {
+ int i = x.elements_size();
+ m_output << "[";
+ for (auto &e : x.elements()) {
+ i--;
+ if (i == 0) {
+ visit(e);
+ } else {
+ visit(e);
+ m_output << ", ";
+ }
+ }
+ m_output << "]";
+ } else {
+ m_output << "[1]";
+ }
+}
+
+void protoConverter::visit(Array const& x)
+{
+ switch (x.arr_func()) {
+ case Array::FLATTEN:
+ visit(x.arr_arg());
+ m_output << ".flatten";
+ break;
+ case Array::COMPACT:
+ visit(x.arr_arg());
+ m_output << ".compact";
+ break;
+ case Array::FETCH:
+ visit(x.arr_arg());
+ m_output << ".fetch";
+ break;
+ case Array::FILL:
+ visit(x.arr_arg());
+ m_output << ".fill";
+ break;
+ case Array::ROTATE:
+ visit(x.arr_arg());
+ m_output << ".rotate";
+ break;
+ case Array::ROTATE_E:
+ visit(x.arr_arg());
+ m_output << ".rotate!";
+ break;
+ case Array::DELETEIF:
+ visit(x.arr_arg());
+ m_output << ".delete_if";
+ break;
+ case Array::INSERT:
+ visit(x.arr_arg());
+ m_output << ".insert";
+ break;
+ case Array::BSEARCH:
+ visit(x.arr_arg());
+ m_output << ".bsearch";
+ break;
+ case Array::KEEPIF:
+ visit(x.arr_arg());
+ m_output << ".keep_if";
+ break;
+ case Array::SELECT:
+ visit(x.arr_arg());
+ m_output << ".select";
+ break;
+ case Array::VALUES_AT:
+ visit(x.arr_arg());
+ m_output << ".values_at";
+ break;
+ case Array::BLOCK:
+ visit(x.arr_arg());
+ m_output << ".index";
+ break;
+ case Array::DIG:
+ visit(x.arr_arg());
+ m_output << ".dig";
+ break;
+ case Array::SLICE:
+ visit(x.arr_arg());
+ m_output << ".slice";
+ break;
+ case Array::PERM:
+ visit(x.arr_arg());
+ m_output << ".permutation";
+ break;
+ case Array::COMB:
+ visit(x.arr_arg());
+ m_output << ".combination";
+ break;
+ case Array::ASSOC:
+ visit(x.arr_arg());
+ m_output << ".assoc";
+ break;
+ case Array::RASSOC:
+ visit(x.arr_arg());
+ m_output << ".rassoc";
+ break;
+ }
+ m_output << "(";
+ visit(x.val_arg());
+ m_output << ")";
+}
+
+void protoConverter::visit(AssignmentStatement const& x)
+{
+ m_output << "var_" << m_numLiveVars << " = ";
+ visit(x.rvalue());
+ m_numVarsPerScope.top()++;
+ m_numLiveVars++;
+ m_output << "\n";
+}
+
+void protoConverter::visit(BinaryOp const& x)
+{
+ m_output << "(";
+ visit(x.left());
+ switch (x.op()) {
+ case BinaryOp::ADD: m_output << " + "; break;
+ case BinaryOp::SUB: m_output << " - "; break;
+ case BinaryOp::MUL: m_output << " * "; break;
+ case BinaryOp::DIV: m_output << " / "; break;
+ case BinaryOp::MOD: m_output << " % "; break;
+ case BinaryOp::XOR: m_output << " ^ "; break;
+ case BinaryOp::AND: m_output << " and "; break;
+ case BinaryOp::OR: m_output << " or "; break;
+ case BinaryOp::EQ: m_output << " == "; break;
+ case BinaryOp::NE: m_output << " != "; break;
+ case BinaryOp::LE: m_output << " <= "; break;
+ case BinaryOp::GE: m_output << " >= "; break;
+ case BinaryOp::LT: m_output << " < "; break;
+ case BinaryOp::GT: m_output << " > "; break;
+ case BinaryOp::RS: m_output << " >> "; break;
+ }
+ visit(x.right());
+ m_output << ")";
+}
+
+void protoConverter::visit(BuiltinFuncs const& x)
+{
+ switch (x.bifunc_oneof_case()) {
+ case BuiltinFuncs::kOs:
+ visit(x.os());
+ break;
+ case BuiltinFuncs::kTime:
+ visit(x.time());
+ break;
+ case BuiltinFuncs::kArr:
+ visit(x.arr());
+ break;
+ case BuiltinFuncs::kMops:
+ visit(x.mops());
+ break;
+ case BuiltinFuncs::BIFUNC_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+ m_output << "\n";
+}
+
+void protoConverter::visit(Const const& x)
+{
+ switch (x.const_oneof_case()) {
+ case Const::kIntLit:
+ m_output << "(" << (x.int_lit() % 13) << ")";
+ break;
+ case Const::kBoolVal:
+ m_output << "(" << x.bool_val() << ")";
+ break;
+ case Const::CONST_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+}
+
+void protoConverter::visit(Function const& x)
+{
+ m_output << "def foo()\nvar_0 = 1\n";
+ visit(x.statements());
+ m_output << "end\n";
+ m_output << "foo\n";
+}
+
+void protoConverter::visit(HashType const& x)
+{
+ if (x.keyval_size() > 0) {
+ int i = x.keyval_size();
+ m_output << "{";
+ for (auto &e : x.keyval()) {
+ i--;
+ if (i == 0) {
+ visit(e);
+ }
+ else {
+ visit(e);
+ m_output << ", ";
+ }
+ }
+ m_output << "}";
+ }
+}
+
+void protoConverter::visit(IfElse const& x)
+{
+ m_output << "if ";
+ visit(x.cond());
+ m_output << "\n";
+ visit(x.if_body());
+ m_output << "\nelse\n";
+ visit(x.else_body());
+ m_output << "\nend\n";
+}
+
+void protoConverter::visit(KVPair const& x)
+{
+ m_output << "\"" << removeSpecial(x.key()) << "\"";
+ m_output << " => ";
+ m_output << "\"" << removeSpecial(x.val()) << "\"";
+}
+
+void protoConverter::visit(MathConst const& x)
+{
+ switch (x.math_const()) {
+ case MathConst::PI:
+ m_output << "Math::PI";
+ break;
+ case MathConst::E:
+ m_output << "Math::E";
+ break;
+ }
+}
+
+void protoConverter::visit(MathOps const& x)
+{
+ switch (x.math_op()) {
+ case MathOps::CBRT:
+ m_output << "Math.cbrt(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::COS:
+ m_output << "Math.cos(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::ERF:
+ m_output << "Math.erf(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::ERFC:
+ m_output << "Math.erfc(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::LOG:
+ m_output << "Math.log(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::LOG10:
+ m_output << "Math.log10(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::LOG2:
+ m_output << "Math.log2(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::SIN:
+ m_output << "Math.sin(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::SQRT:
+ m_output << "Math.sqrt(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::TAN:
+ m_output << "Math.tan(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ }
+}
+
+void protoConverter::visit(MathType const& x)
+{
+ switch (x.math_arg_oneof_case()) {
+ case MathType::kMathRval:
+ visit(x.math_rval());
+ break;
+ case MathType::kMathConst:
+ visit(x.math_const());
+ break;
+ case MathType::MATH_ARG_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+}
+
+void protoConverter::visit(ObjectSpace const& x)
+{
+ switch (x.os_func()) {
+ case ObjectSpace::COUNT:
+ m_output << "ObjectSpace.count_objects";
+ break;
+ }
+ m_output << "(";
+ visit(x.os_arg());
+ m_output << ")" << "\n";
+}
+
+void protoConverter::visit(Rvalue const& x)
+{
+ switch (x.rvalue_oneof_case()) {
+ case Rvalue::kVarref:
+ visit(x.varref());
+ break;
+ case Rvalue::kCons:
+ visit(x.cons());
+ break;
+ case Rvalue::kBinop:
+ visit(x.binop());
+ break;
+ case Rvalue::RVALUE_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+}
+
+void protoConverter::visit(Statement const& x)
+{
+ switch (x.stmt_oneof_case()) {
+ case Statement::kAssignment:
+ visit(x.assignment());
+ break;
+ case Statement::kIfelse:
+ visit(x.ifelse());
+ break;
+ case Statement::kTernaryStmt:
+ visit(x.ternary_stmt());
+ break;
+ case Statement::kBuiltins:
+ visit(x.builtins());
+ break;
+ case Statement::kBlockstmt:
+ visit(x.blockstmt());
+ break;
+ case Statement::STMT_ONEOF_NOT_SET:
+ break;
+ }
+ m_output << "\n";
+}
+
+void protoConverter::visit(StatementSeq const& x)
+{
+ if (x.statements_size() > 0) {
+ m_numVarsPerScope.push(0);
+ m_output << "@scope ||= begin\n";
+ for (auto &st : x.statements())
+ visit(st);
+ m_output << "end\n";
+ m_numLiveVars -= m_numVarsPerScope.top();
+ m_numVarsPerScope.pop();
+ }
+}
+
+void protoConverter::visit(StringExtNoArg const& x)
+{
+ m_output << "\"" << removeSpecial(x.str_arg()) << "\"";
+ switch (x.str_op()) {
+ case StringExtNoArg::DUMP:
+ m_output << ".dump";
+ break;
+ case StringExtNoArg::STRIP:
+ m_output << ".strip";
+ break;
+ case StringExtNoArg::LSTRIP:
+ m_output << ".lstrip";
+ break;
+ case StringExtNoArg::RSTRIP:
+ m_output << ".rstrip";
+ break;
+ case StringExtNoArg::STRIPE:
+ m_output << ".strip!";
+ break;
+ case StringExtNoArg::LSTRIPE:
+ m_output << ".lstrip!";
+ break;
+ case StringExtNoArg::RSTRIPE:
+ m_output << ".rstrip!";
+ break;
+ case StringExtNoArg::SWAPCASE:
+ m_output << ".swapcase";
+ break;
+ case StringExtNoArg::SWAPCASEE:
+ m_output << ".swapcase!";
+ break;
+ case StringExtNoArg::SQUEEZE:
+ m_output << ".squeeze";
+ break;
+ }
+}
+
+void protoConverter::visit(Ternary const& x)
+{
+ m_output << "(";
+ visit(x.tern_cond());
+ m_output << " ? ";
+ visit(x.t_branch());
+ m_output << " : ";
+ visit(x.f_branch());
+ m_output << ")\n";
+}
+
+void protoConverter::visit(Time const& x)
+{
+ switch (x.t_func()) {
+ case Time::AT:
+ m_output << "Time.at";
+ break;
+ case Time::GM:
+ m_output << "Time.gm";
+ break;
+ }
+ m_output << "(" << (x.t_arg()% 13) << ")" << "\n";
+}
+
+void protoConverter::visit(VarRef const& x)
+{
+ m_output << "var_" << (static_cast<uint32_t>(x.varnum()) % m_numLiveVars);
+}
+
+std::string protoConverter::FunctionToString(Function const& input)
+{
+ visit(input);
+ return m_output.str();
+}
diff --git a/oss-fuzz/proto_to_ruby.h b/oss-fuzz/proto_to_ruby.h
new file mode 100644
index 000000000..355c42839
--- /dev/null
+++ b/oss-fuzz/proto_to_ruby.h
@@ -0,0 +1,55 @@
+#include <cstdint>
+#include <cstddef>
+#include <string>
+#include <ostream>
+#include <sstream>
+#include <stack>
+#include <ruby.pb.h>
+
+namespace ruby_fuzzer {
+ class protoConverter
+ {
+ public:
+ protoConverter() {
+ m_numLiveVars = 1;
+ m_numVarsPerScope.push(m_numLiveVars);
+ }
+ protoConverter(protoConverter const& x) {
+ m_numLiveVars = x.m_numLiveVars;
+ m_numVarsPerScope = x.m_numVarsPerScope;
+ }
+ ~protoConverter() {}
+ std::string FunctionToString(Function const& _input);
+
+ private:
+ void visit(ArrType const&);
+ void visit(Array const&);
+ void visit(AssignmentStatement const&);
+ void visit(BinaryOp const&);
+ void visit(BuiltinFuncs const&);
+ void visit(Const const&);
+ void visit(Function const&);
+ void visit(HashType const&);
+ void visit(IfElse const&);
+ void visit(KVPair const&);
+ void visit(MathConst const&);
+ void visit(MathOps const&);
+ void visit(MathType const&);
+ void visit(ObjectSpace const&);
+ void visit(Rvalue const&);
+ void visit(Statement const&);
+ void visit(StatementSeq const&);
+ void visit(StringExtNoArg const&);
+ void visit(Ternary const&);
+ void visit(Time const&);
+ void visit(VarRef const&);
+ template <class T>
+ void visit(google::protobuf::RepeatedPtrField<T> const& _repeated_field);
+
+ std::string removeSpecial(const std::string &x);
+
+ std::ostringstream m_output;
+ std::stack<uint8_t> m_numVarsPerScope;
+ int32_t m_numLiveVars;
+ };
+}
diff --git a/oss-fuzz/ruby.proto b/oss-fuzz/ruby.proto
new file mode 100644
index 000000000..d9b0804c8
--- /dev/null
+++ b/oss-fuzz/ruby.proto
@@ -0,0 +1,201 @@
+syntax = "proto2";
+
+message VarRef {
+ required int32 varnum = 1;
+}
+
+message ArrType {
+ repeated Const elements = 1;
+}
+
+message KVPair {
+ required string key = 1;
+ required string val = 2;
+}
+
+message HashType {
+ repeated KVPair keyval = 1;
+}
+
+message StringExtNoArg {
+ enum StrExtOp {
+ DUMP = 0;
+ STRIP = 1;
+ LSTRIP = 2;
+ RSTRIP = 3;
+ STRIPE = 4;
+ LSTRIPE = 5;
+ RSTRIPE = 6;
+ SWAPCASE = 7;
+ SWAPCASEE = 8;
+ SQUEEZE = 9;
+ }
+ required StrExtOp str_op = 1;
+ required string str_arg = 2;
+}
+
+message MathConst {
+ enum MathConstLit {
+ PI = 0;
+ E = 1;
+ }
+ required MathConstLit math_const = 1;
+}
+
+message Const {
+ oneof const_oneof {
+ uint32 int_lit = 1;
+ bool bool_val = 4;
+ }
+}
+
+message BinaryOp {
+ enum Op {
+ ADD = 0;
+ SUB = 1;
+ MUL = 2;
+ DIV = 3;
+ MOD = 4;
+ XOR = 5;
+ AND = 6;
+ OR = 7;
+ EQ = 8;
+ NE = 9;
+ LE = 10;
+ GE = 11;
+ LT = 12;
+ GT = 13;
+ RS = 14;
+ };
+ required Op op = 1;
+ required Rvalue left = 2;
+ required Rvalue right = 3;
+}
+
+message Rvalue {
+ oneof rvalue_oneof {
+ VarRef varref = 1;
+ Const cons = 2;
+ BinaryOp binop = 3;
+ }
+}
+
+message AssignmentStatement {
+ required Rvalue rvalue = 2;
+}
+
+
+message IfElse {
+ required Rvalue cond = 1;
+ required StatementSeq if_body = 2;
+ required StatementSeq else_body = 3;
+}
+
+//TODO: Add Switch statement
+//message Switch {
+// required Rvalue switch_var = 1;
+// repeated Rvalue cond = 2;
+//}
+
+message Ternary {
+ required Rvalue tern_cond = 1;
+ required Rvalue t_branch = 2;
+ required Rvalue f_branch = 3;
+}
+
+message ObjectSpace {
+ enum OS_methods {
+ COUNT = 1;
+ }
+ required OS_methods os_func = 1;
+ required HashType os_arg = 2;
+}
+
+message Time {
+ enum T_methods {
+ AT = 1;
+ GM = 2;
+ }
+ required T_methods t_func = 1;
+ required uint32 t_arg = 2;
+}
+
+message Array {
+ enum Arr_methods {
+ FLATTEN = 1;
+ COMPACT = 2;
+ FETCH = 3;
+ FILL = 4;
+ ROTATE = 5;
+ ROTATE_E = 6;
+ DELETEIF = 7;
+ INSERT = 8;
+ BSEARCH = 9;
+ KEEPIF = 10;
+ SELECT = 11;
+ VALUES_AT = 12;
+ BLOCK = 13;
+ DIG = 14;
+ SLICE = 15;
+ PERM = 16;
+ COMB = 17;
+ ASSOC = 18;
+ RASSOC = 19;
+ }
+ required Arr_methods arr_func = 1;
+ required ArrType arr_arg = 2;
+ required Rvalue val_arg = 3;
+}
+
+message MathType {
+ oneof math_arg_oneof {
+ Rvalue math_rval = 2;
+ MathConst math_const = 3;
+ }
+}
+
+message MathOps {
+ enum Mops {
+ CBRT = 1;
+ COS = 2;
+ ERF = 3;
+ ERFC = 4;
+ LOG = 5;
+ LOG10 = 6;
+ LOG2 = 7;
+ SIN = 8;
+ SQRT = 9;
+ TAN = 10;
+ }
+ required Mops math_op = 1;
+ required MathType math_arg = 2;
+}
+
+message BuiltinFuncs {
+ oneof bifunc_oneof {
+ ObjectSpace os = 1;
+ Time time = 2;
+ Array arr = 3;
+ MathOps mops = 4;
+ }
+}
+
+message Statement {
+ oneof stmt_oneof {
+ AssignmentStatement assignment = 1;
+ IfElse ifelse = 2;
+ Ternary ternary_stmt = 3;
+ BuiltinFuncs builtins = 4;
+ StatementSeq blockstmt = 5;
+ }
+}
+
+message StatementSeq {
+ repeated Statement statements = 1;
+}
+
+message Function {
+ required StatementSeq statements = 1;
+}
+
+package ruby_fuzzer;
diff --git a/src/array.c b/src/array.c
index 0b039a6ec..bb7190bfd 100644
--- a/src/array.c
+++ b/src/array.c
@@ -9,6 +9,8 @@
#include <mruby/class.h>
#include <mruby/string.h>
#include <mruby/range.h>
+#include <mruby/proc.h>
+#include <mruby/presym.h>
#include "value_array.h"
#define ARY_DEFAULT_LEN 4
@@ -27,7 +29,7 @@ ary_new_capa(mrb_state *mrb, mrb_int capa)
}
blen = capa * sizeof(mrb_value);
- a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
+ a = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class);
if (capa <= MRB_ARY_EMBED_LEN_MAX) {
ARY_SET_EMBED_LEN(a, 0);
}
@@ -54,18 +56,17 @@ mrb_ary_new(mrb_state *mrb)
}
/*
- * to copy array, use this instead of memcpy because of portability
+ * 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
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56620
* * gcc on MIPS also fail
- * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755
+ * https://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
- *
+ * See also https://togetter.com/li/462898 (Japanese)
*/
static inline void
array_copy(mrb_value *dst, const mrb_value *src, mrb_int size)
@@ -120,9 +121,7 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size)
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");
- }
+ mrb_check_frozen(mrb, a);
}
static void
@@ -280,7 +279,7 @@ static mrb_value
mrb_ary_s_create(mrb_state *mrb, mrb_value klass)
{
mrb_value ary;
- mrb_value *vals;
+ const mrb_value *vals;
mrb_int len;
struct RArray *a;
@@ -340,7 +339,7 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self)
{
struct RArray *a1 = mrb_ary_ptr(self);
struct RArray *a2;
- mrb_value *ptr;
+ const mrb_value *ptr;
mrb_int blen, len1;
mrb_get_args(mrb, "a", &ptr, &blen);
@@ -388,7 +387,7 @@ ary_replace(mrb_state *mrb, struct RArray *a, struct RArray *b)
mrb_write_barrier(mrb, (struct RBasic*)a);
return;
}
- if (!MRB_FROZEN_P(b) && len > ARY_REPLACE_SHARED_MIN) {
+ if (!mrb_frozen_p(b) && len > ARY_REPLACE_SHARED_MIN) {
ary_make_shared(mrb, b);
goto shared_b;
}
@@ -508,22 +507,26 @@ mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem)
static mrb_value
mrb_ary_push_m(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv;
- mrb_int len, len2, alen;
+ mrb_int argc;
+ const mrb_value *argv;
+ mrb_int len, len2;
struct RArray *a;
- mrb_get_args(mrb, "*!", &argv, &alen);
+ argc = mrb_get_argc(mrb);
+ argv = mrb_get_argv(mrb);
a = mrb_ary_ptr(self);
ary_modify(mrb, a);
len = ARY_LEN(a);
- len2 = len + alen;
+ len2 = len + argc;
if (ARY_CAPA(a) < len2) {
ary_expand_capa(mrb, a, len2);
}
- array_copy(ARY_PTR(a)+len, argv, alen);
+ array_copy(ARY_PTR(a)+len, argv, argc);
ARY_SET_LEN(a, len2);
- mrb_write_barrier(mrb, (struct RBasic*)a);
-
+ while (argc--) {
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)a, *argv);
+ argv++;
+ }
return self;
}
@@ -575,6 +578,48 @@ mrb_ary_shift(mrb_state *mrb, mrb_value self)
return val;
}
+static mrb_value
+mrb_ary_shift_m(mrb_state *mrb, mrb_value self)
+{
+ struct RArray *a = mrb_ary_ptr(self);
+ mrb_int len = ARY_LEN(a);
+ mrb_int n;
+ mrb_value val;
+
+ if (mrb_get_args(mrb, "|i", &n) == 0) {
+ return mrb_ary_shift(mrb, self);
+ };
+ ary_modify_check(mrb, a);
+ if (len == 0 || n == 0) return mrb_ary_new(mrb);
+ if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array shift");
+ if (n > len) n = len;
+ val = mrb_ary_new_from_values(mrb, n, ARY_PTR(a));
+ if (ARY_SHARED_P(a)) {
+ L_SHIFT:
+ a->as.heap.ptr+=n;
+ a->as.heap.len-=n;
+ return val;
+ }
+ if (len > ARY_SHIFT_SHARED_MIN) {
+ ary_make_shared(mrb, a);
+ goto L_SHIFT;
+ }
+ else if (len == n) {
+ ARY_SET_LEN(a, 0);
+ }
+ else {
+ mrb_value *ptr = ARY_PTR(a);
+ mrb_int size = len-n;
+
+ while (size--) {
+ *ptr = *(ptr+n);
+ ++ptr;
+ }
+ ARY_SET_LEN(a, len-n);
+ }
+ return val;
+}
+
/* self = [1,2,3]
item = 0
self.unshift item
@@ -611,7 +656,8 @@ static mrb_value
mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_value *vals, *ptr;
+ const mrb_value *vals;
+ mrb_value *ptr;
mrb_int alen, len;
mrb_get_args(mrb, "*!", &vals, &alen);
@@ -631,11 +677,13 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
ptr = a->as.heap.ptr;
}
else {
+ mrb_bool same = vals == ARY_PTR(a);
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);
+ if (same) vals = ptr;
}
array_copy(ptr, vals, alen);
ARY_SET_LEN(a, len+alen);
@@ -646,19 +694,6 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
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)
{
@@ -670,7 +705,7 @@ mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
if (n < 0) {
n += len;
if (n < 0) {
- mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of array", n - len);
}
}
if (len <= n) {
@@ -702,7 +737,7 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
ary_modify(mrb, a);
/* len check */
- if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%S)", mrb_fixnum_value(len));
+ if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%i)", len);
/* range check */
if (head < 0) {
@@ -730,13 +765,17 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
argv = ARY_PTR(r);
}
}
+ else if (mrb_undef_p(rpl)) {
+ argc = 0;
+ argv = NULL;
+ }
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));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", head);
}
len = head + argc;
if (len > ARY_CAPA(a)) {
@@ -752,7 +791,7 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
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));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", alen + argc - len);
}
newlen = alen + argc - len;
if (newlen > ARY_CAPA(a)) {
@@ -792,7 +831,7 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len)
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 = 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;
@@ -802,20 +841,27 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len)
return mrb_obj_value(b);
}
+mrb_value
+mrb_ary_subseq(mrb_state *mrb, mrb_value ary, mrb_int beg, mrb_int len)
+{
+ struct RArray *a = mrb_ary_ptr(ary);
+ return ary_subseq(mrb, a, beg, len);
+}
+
static mrb_int
aget_index(mrb_state *mrb, mrb_value index)
{
- if (mrb_fixnum_p(index)) {
- return mrb_fixnum(index);
+ if (mrb_integer_p(index)) {
+ return mrb_integer(index);
}
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
else if (mrb_float_p(index)) {
return (mrb_int)mrb_float(index);
}
#endif
else {
mrb_int i, argc;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_get_args(mrb, "i*!", &i, &argv, &argc);
return i;
@@ -853,26 +899,29 @@ 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_int i;
+ mrb_int len, alen;
mrb_value index;
- if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
+ if (mrb_get_argc(mrb) == 1) {
+ index = mrb_get_arg1(mrb);
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) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
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));
+ case MRB_TT_INTEGER:
+ return mrb_ary_ref(mrb, self, mrb_integer(index));
default:
return mrb_ary_ref(mrb, self, aget_index(mrb, index));
}
}
+ mrb_get_args(mrb, "oi", &index, &len);
i = aget_index(mrb, index);
alen = ARY_LEN(a);
if (i < 0) i += alen;
@@ -925,23 +974,27 @@ 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) {
+ ary_modify(mrb, mrb_ary_ptr(self));
+ if (mrb_get_argc(mrb) == 2) {
+ const mrb_value *vs = mrb_get_argv(mrb);
+ v1 = vs[0]; v2 = vs[1];
+
/* a[n..m] = v */
switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) {
- case 0: /* not range */
+ case MRB_RANGE_TYPE_MISMATCH:
mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
break;
- case 1: /* range */
+ case MRB_RANGE_OK:
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);
+ case MRB_RANGE_OUT:
+ mrb_raisef(mrb, E_RANGE_ERROR, "%v out of range", v1);
break;
}
return v2;
}
+ mrb_get_args(mrb, "ooo", &v1, &v2, &v3);
/* a[n,m] = v */
mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3);
return v3;
@@ -1025,10 +1078,9 @@ mrb_ary_last(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_ary_index_m(mrb_state *mrb, mrb_value self)
{
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
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);
@@ -1040,10 +1092,9 @@ mrb_ary_index_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
{
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
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);
@@ -1058,33 +1109,26 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
MRB_API mrb_value
mrb_ary_splat(mrb_state *mrb, mrb_value v)
{
- mrb_value a, recv_class;
+ mrb_value ary;
+ struct RArray *a;
if (mrb_array_p(v)) {
- return v;
+ a = ary_dup(mrb, mrb_ary_ptr(v));
+ return mrb_obj_value(a);
}
- if (!mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) {
+ if (!mrb_respond_to(mrb, v, MRB_SYM(to_a))) {
return mrb_ary_new_from_values(mrb, 1, &v);
}
- a = mrb_funcall(mrb, v, "to_a", 0);
- if (mrb_array_p(a)) {
- return a;
- }
- else if (mrb_nil_p(a)) {
+ ary = mrb_funcall_id(mrb, v, MRB_SYM(to_a), 0);
+ if (mrb_nil_p(ary)) {
return mrb_ary_new_from_values(mrb, 1, &v);
}
- else {
- recv_class = mrb_obj_value(mrb_obj_class(mrb, v));
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Array (%S#to_a gives %S)",
- recv_class,
- recv_class,
- mrb_obj_value(mrb_obj_class(mrb, a))
- );
- /* not reached */
- return mrb_undef_value();
- }
+ mrb_ensure_array_type(mrb, ary);
+ a = mrb_ary_ptr(ary);
+ a = ary_dup(mrb, a);
+ return mrb_obj_value(a);
}
static mrb_value
@@ -1108,12 +1152,24 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self)
else if (!ARY_EMBED_P(a)){
mrb_free(mrb, a->as.heap.ptr);
}
- ARY_SET_EMBED_LEN(a, 0);
-
+ if (MRB_ARY_EMBED_LEN_MAX > 0) {
+ ARY_SET_EMBED_LEN(a, 0);
+ }
+ else {
+ a->as.heap.ptr = NULL;
+ a->as.heap.aux.capa = 0;
+ ARY_SET_LEN(a, 0);
+ }
return self;
}
static mrb_value
+mrb_ary_clear_m(mrb_state *mrb, mrb_value self)
+{
+ return mrb_ary_clear(mrb, self);
+}
+
+static mrb_value
mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
@@ -1122,21 +1178,16 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
}
MRB_API mrb_value
-mrb_check_array_type(mrb_state *mrb, mrb_value ary)
+mrb_ary_entry(mrb_value ary, mrb_int n)
{
- return mrb_check_convert_type(mrb, ary, MRB_TT_ARRAY, "Array", "to_ary");
-}
+ struct RArray *a = mrb_ary_ptr(ary);
+ mrb_int len = ARY_LEN(a);
-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];
+ /* range check */
+ if (n < 0) n += len;
+ if (n < 0 || len <= n) return mrb_nil_value();
+
+ return ARY_PTR(a)[n];
}
static mrb_value
@@ -1180,7 +1231,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
val = tmp;
goto str_join;
}
- tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+ tmp = mrb_check_array_type(mrb, val);
if (!mrb_nil_p(tmp)) {
val = tmp;
goto ary_join;
@@ -1228,9 +1279,9 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary)
static mrb_value
mrb_ary_eq(mrb_state *mrb, mrb_value ary1)
{
- mrb_value ary2;
+ mrb_value ary2 = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &ary2);
+ mrb->c->ci->mid = 0;
if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value();
if (!mrb_array_p(ary2)) {
return mrb_false_value();
@@ -1243,9 +1294,9 @@ mrb_ary_eq(mrb_state *mrb, mrb_value ary1)
static mrb_value
mrb_ary_cmp(mrb_state *mrb, mrb_value ary1)
{
- mrb_value ary2;
+ mrb_value ary2 = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &ary2);
+ mrb->c->ci->mid = 0;
if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0);
if (!mrb_array_p(ary2)) {
return mrb_nil_value();
@@ -1258,7 +1309,6 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1)
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();
@@ -1274,41 +1324,39 @@ 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->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_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_ARG(1,1)); /* 15.2.12.5.4 */
+ mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ARG(2,1)); /* 15.2.12.5.5 */
+ mrb_define_method(mrb, a, "clear", mrb_ary_clear_m, 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_OPT(1)); /* 15.2.12.5.17 */
+ mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_OPT(1)); /* 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, "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_m, MRB_ARGS_OPT(1)); /* 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_ARG(1,1)); /* 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, "__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, "__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());
}
diff --git a/src/backtrace.c b/src/backtrace.c
index 57ae7fd7f..65cb0da1c 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -14,128 +14,112 @@
#include <mruby/error.h>
#include <mruby/numeric.h>
#include <mruby/data.h>
+#include <mruby/presym.h>
struct backtrace_location {
- int lineno;
- const char *filename;
+ int32_t lineno;
mrb_sym method_id;
+ const char *filename;
};
-typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location*, void*);
+typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*, void*);
static const mrb_data_type bt_type = { "Backtrace", mrb_free };
+mrb_value mrb_exc_inspect(mrb_state *mrb, mrb_value exc);
+mrb_value mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace);
+
static void
-each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
+each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, 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++) {
+ for (ptrdiff_t i=ciidx; i >= 0; i--) {
struct backtrace_location loc;
mrb_callinfo *ci;
- mrb_irep *irep;
- mrb_code *pc;
+ const mrb_irep *irep = 0;
+ const mrb_code *pc;
+ uint32_t idx;
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];
+ if (!ci->proc || MRB_PROC_CFUNC_P(ci->proc)) {
+ if (!ci->mid) continue;
+ loc.lineno = -1;
+ idx = 0;
}
else {
- pc = pc0;
+ irep = ci->proc->body.irep;
+ if (!irep) continue;
+ if (mrb->c->cibase[i].pc) {
+ pc = &mrb->c->cibase[i].pc[-1];
+ }
+ else {
+ continue;
+ }
+ idx = (uint32_t)(pc - irep->iseq);
+ loc.lineno = mrb_debug_get_line(mrb, irep, idx);
+ }
+ loc.method_id = ci->mid;
+ if (loc.lineno == -1) {
+ for (ptrdiff_t j=i-1; j >= 0; j--) {
+ ci = &mrb->c->cibase[j];
+
+ if (!ci->proc) continue;
+ if (MRB_PROC_CFUNC_P(ci->proc)) continue;
+
+ irep = ci->proc->body.irep;
+ if (!irep) continue;
+
+ if (mrb->c->cibase[j].pc) {
+ pc = &mrb->c->cibase[j].pc[-1];
+ }
+ else {
+ continue;
+ }
+
+ idx = (uint32_t)(pc - irep->iseq);
+ loc.lineno = mrb_debug_get_line(mrb, irep, idx);
+ if (loc.lineno > 0) break;
+ }
}
- 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;
+ loc.filename = mrb_debug_get_filename(mrb, irep, idx);
if (!loc.filename) {
loc.filename = "(unknown)";
}
- loc.method_id = ci->mid;
func(mrb, &loc, data);
}
}
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
static void
-print_backtrace(mrb_state *mrb, mrb_value backtrace)
+print_backtrace(mrb_state *mrb, struct RObject *exc, mrb_value backtrace)
{
- int i;
- mrb_int n;
+ mrb_int i;
+ mrb_int n = RARRAY_LEN(backtrace);
+ mrb_value *loc, mesg;
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; i<n; i++) {
- mrb_value entry = RARRAY_PTR(backtrace)[n-i-1];
-
- if (mrb_string_p(entry)) {
- fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry));
+ if (n != 0) {
+ if (n > 1) {
+ fprintf(stream, "trace (most recent call last):\n");
}
- }
-}
-
-static int
-packed_bt_len(struct backtrace_location *bt, int n)
-{
- int len = 0;
- int i;
-
- for (i=0; i<n; i++) {
- if (!bt[i].filename && !bt[i].lineno && !bt[i].method_id)
- continue;
- len++;
- }
- return len;
-}
-
-static void
-print_packed_backtrace(mrb_state *mrb, mrb_value packed)
-{
- FILE *stream = stderr;
- struct backtrace_location *bt;
- int n, i;
- int ai = mrb_gc_arena_save(mrb);
-
- bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, packed, &bt_type);
- if (bt == NULL) return;
- n = (mrb_int)RDATA(packed)->flags;
-
- if (packed_bt_len(bt, n) == 0) return;
- fprintf(stream, "trace (most recent call last):\n");
- for (i = 0; i<n; i++) {
- struct backtrace_location *entry = &bt[n-i-1];
- if (entry->filename == 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);
+ for (i=n-1,loc=&RARRAY_PTR(backtrace)[i]; i>0; i--,loc--) {
+ if (mrb_string_p(*loc)) {
+ fprintf(stream, "\t[%d] %.*s\n",
+ (int)i, (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
+ }
+ }
+ if (mrb_string_p(*loc)) {
+ fprintf(stream, "%.*s: ", (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
}
- fprintf(stream, "\n");
}
+ mesg = mrb_exc_inspect(mrb, mrb_obj_value(exc));
+ fprintf(stream, "%.*s\n", (int)RSTRING_LEN(mesg), RSTRING_PTR(mesg));
}
/* mrb_print_backtrace
@@ -152,14 +136,10 @@ mrb_print_backtrace(mrb_state *mrb)
return;
}
- backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace"));
+ backtrace = mrb_obj_iv_get(mrb, mrb->exc, MRB_SYM(backtrace));
if (mrb_nil_p(backtrace)) return;
- if (mrb_array_p(backtrace)) {
- print_backtrace(mrb, backtrace);
- }
- else {
- print_packed_backtrace(mrb, backtrace);
- }
+ if (!mrb_array_p(backtrace)) backtrace = mrb_unpack_backtrace(mrb, backtrace);
+ print_backtrace(mrb, mrb->exc, backtrace);
}
#else
@@ -172,24 +152,22 @@ mrb_print_backtrace(mrb_state *mrb)
static void
count_backtrace_i(mrb_state *mrb,
- struct backtrace_location *loc,
+ const 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,
+ const 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;
}
@@ -203,20 +181,19 @@ packed_backtrace(mrb_state *mrb)
int size;
void *ptr;
- each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
+ each_backtrace(mrb, ciidx, 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);
+ backtrace->flags = (uint32_t)len;
+ each_backtrace(mrb, ciidx, 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_sym sym = MRB_SYM(backtrace);
mrb_value backtrace;
int ai;
@@ -230,7 +207,7 @@ mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
mrb_value
mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
{
- struct backtrace_location *bt;
+ const struct backtrace_location *bt;
mrb_int n, i;
int ai;
@@ -242,19 +219,22 @@ mrb_unpack_backtrace(mrb_state *mrb, mrb_value 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;
+ if (n == 0) goto empty_backtrace;
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];
+ const 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->lineno != -1) {//debug info was available
+ btline = mrb_format(mrb, "%s:%d", entry->filename, (int)entry->lineno);
+ }
+ else { //all that was left was the stack frame
+ btline = mrb_format(mrb, "%s:0", entry->filename);
+ }
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_str_cat_cstr(mrb, btline, mrb_sym_name(mrb, entry->method_id));
}
mrb_ary_push(mrb, backtrace, btline);
mrb_gc_arena_restore(mrb, ai);
@@ -263,23 +243,22 @@ mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
return backtrace;
}
-MRB_API mrb_value
+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);
+ backtrace = mrb_iv_get(mrb, exc, MRB_SYM(backtrace));
if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) {
return backtrace;
}
+ /* unpack packed-backtrace */
backtrace = mrb_unpack_backtrace(mrb, backtrace);
- mrb_iv_set(mrb, exc, attr_name, backtrace);
+ mrb_iv_set(mrb, exc, MRB_SYM(backtrace), backtrace);
return backtrace;
}
-MRB_API mrb_value
+mrb_value
mrb_get_backtrace(mrb_state *mrb)
{
return mrb_unpack_backtrace(mrb, packed_backtrace(mrb));
diff --git a/src/cdump.c b/src/cdump.c
new file mode 100644
index 000000000..ecc27d9a1
--- /dev/null
+++ b/src/cdump.c
@@ -0,0 +1,468 @@
+/*
+** dump.c - mruby binary dumper (mrbc binary format)
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/dump.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
+
+#include <string.h>
+
+#ifndef MRB_NO_STDIO
+
+#ifndef MRB_NO_FLOAT
+#include <mruby/endian.h>
+#define MRB_FLOAT_FMT "%.17g"
+#endif
+
+static int
+cdump_pool(mrb_state *mrb, const mrb_pool_value *p, FILE *fp)
+{
+ if (p->tt & IREP_TT_NFLAG) { /* number */
+ switch (p->tt) {
+#ifdef MRB_64BIT
+ case IREP_TT_INT64:
+ if (p->u.i64 < INT32_MIN || INT32_MAX < p->u.i64) {
+ fprintf(fp, "{IREP_TT_INT64, {.i64=%" PRId64 "}},\n", p->u.i64);
+ }
+ else {
+ fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", (int32_t)p->u.i64);
+ }
+ break;
+#endif
+ case IREP_TT_INT32:
+ fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", p->u.i32);
+ break;
+ case IREP_TT_FLOAT:
+#ifndef MRB_NO_FLOAT
+ if (p->u.f == 0) {
+ fprintf(fp, "{IREP_TT_FLOAT, {.f=%#.1f}},\n", p->u.f);
+ }
+ else {
+ fprintf(fp, "{IREP_TT_FLOAT, {.f=" MRB_FLOAT_FMT "}},\n", p->u.f);
+ }
+#endif
+ break;
+ case IREP_TT_BIGINT:
+ {
+ const char *s = p->u.str;
+ int len = s[0]+2;
+ fputs("{IREP_TT_BIGINT, {\"", fp);
+ for (int i=0; i<len; i++) {
+ fprintf(fp, "\\x%02x", (int)s[i]&0xff);
+ }
+ fputs("\"}},\n", fp);
+ }
+ break;
+ }
+ }
+ else { /* string */
+ int i, len = p->tt>>2;
+ const char *s = p->u.str;
+ fprintf(fp, "{IREP_TT_STR|(%d<<2), {\"", len);
+ for (i=0; i<len; i++) {
+ fprintf(fp, "\\x%02x", (int)s[i]&0xff);
+ }
+ fputs("\"}},\n", fp);
+ }
+ return MRB_DUMP_OK;
+}
+
+static mrb_bool
+sym_name_word_p(const char *name, mrb_int len)
+{
+ if (len == 0) return FALSE;
+ if (name[0] != '_' && !ISALPHA(name[0])) return FALSE;
+ for (int i = 1; i < len; i++) {
+ if (name[i] != '_' && !ISALNUM(name[i])) return FALSE;
+ }
+ return TRUE;
+}
+
+static mrb_bool
+sym_name_with_equal_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[len-1] == '=' && sym_name_word_p(name, len-1);
+}
+
+static mrb_bool
+sym_name_with_question_mark_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[len-1] == '?' && sym_name_word_p(name, len-1);
+}
+
+static mrb_bool
+sym_name_with_bang_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[len-1] == '!' && sym_name_word_p(name, len-1);
+}
+
+static mrb_bool
+sym_name_ivar_p(const char *name, mrb_int len)
+{
+ return len >= 2 && name[0] == '@' && sym_name_word_p(name+1, len-1);
+}
+
+static mrb_bool
+sym_name_cvar_p(const char *name, mrb_int len)
+{
+ return len >= 3 && name[0] == '@' && sym_name_ivar_p(name+1, len-1);
+}
+
+#define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1}
+struct operator_symbol {
+ const char *name;
+ const char *sym_name;
+ uint16_t sym_name_len;
+};
+static const struct operator_symbol operator_table[] = {
+ OPERATOR_SYMBOL("!", "not"),
+ OPERATOR_SYMBOL("%", "mod"),
+ OPERATOR_SYMBOL("&", "and"),
+ OPERATOR_SYMBOL("*", "mul"),
+ OPERATOR_SYMBOL("+", "add"),
+ OPERATOR_SYMBOL("-", "sub"),
+ OPERATOR_SYMBOL("/", "div"),
+ OPERATOR_SYMBOL("<", "lt"),
+ OPERATOR_SYMBOL(">", "gt"),
+ OPERATOR_SYMBOL("^", "xor"),
+ OPERATOR_SYMBOL("`", "tick"),
+ OPERATOR_SYMBOL("|", "or"),
+ OPERATOR_SYMBOL("~", "neg"),
+ OPERATOR_SYMBOL("!=", "neq"),
+ OPERATOR_SYMBOL("!~", "nmatch"),
+ OPERATOR_SYMBOL("&&", "andand"),
+ OPERATOR_SYMBOL("**", "pow"),
+ OPERATOR_SYMBOL("+@", "plus"),
+ OPERATOR_SYMBOL("-@", "minus"),
+ OPERATOR_SYMBOL("<<", "lshift"),
+ OPERATOR_SYMBOL("<=", "le"),
+ OPERATOR_SYMBOL("==", "eq"),
+ OPERATOR_SYMBOL("=~", "match"),
+ OPERATOR_SYMBOL(">=", "ge"),
+ OPERATOR_SYMBOL(">>", "rshift"),
+ OPERATOR_SYMBOL("[]", "aref"),
+ OPERATOR_SYMBOL("||", "oror"),
+ OPERATOR_SYMBOL("<=>", "cmp"),
+ OPERATOR_SYMBOL("===", "eqq"),
+ OPERATOR_SYMBOL("[]=", "aset"),
+};
+
+static const char*
+sym_operator_name(const char *sym_name, mrb_int len)
+{
+ mrb_sym table_size = sizeof(operator_table)/sizeof(struct operator_symbol);
+ if (operator_table[table_size-1].sym_name_len < len) return NULL;
+
+ mrb_sym start, idx;
+ int cmp;
+ const struct operator_symbol *op_sym;
+ for (start = 0; table_size != 0; table_size/=2) {
+ idx = start+table_size/2;
+ op_sym = &operator_table[idx];
+ cmp = (int)len-(int)op_sym->sym_name_len;
+ if (cmp == 0) {
+ cmp = memcmp(sym_name, op_sym->sym_name, len);
+ if (cmp == 0) return op_sym->name;
+ }
+ if (0 < cmp) {
+ start = ++idx;
+ --table_size;
+ }
+ }
+ return NULL;
+}
+
+static const char*
+sym_var_name(mrb_state *mrb, const char *initname, const char *key, int n)
+{
+ char buf[32];
+ mrb_value s = mrb_str_new_cstr(mrb, initname);
+ mrb_str_cat_lit(mrb, s, "_");
+ mrb_str_cat_cstr(mrb, s, key);
+ mrb_str_cat_lit(mrb, s, "_");
+ snprintf(buf, sizeof(buf), "%d", n);
+ mrb_str_cat_cstr(mrb, s, buf);
+ return RSTRING_PTR(s);
+}
+
+static int
+cdump_sym(mrb_state *mrb, mrb_sym sym, const char *var_name, int idx, mrb_value init_syms_code, FILE *fp)
+{
+ if (sym == 0) return MRB_DUMP_INVALID_ARGUMENT;
+
+ mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, sym, &len), *op_name;
+ if (!name) return MRB_DUMP_INVALID_ARGUMENT;
+ if (sym_name_word_p(name, len)) {
+ fprintf(fp, "MRB_SYM(%s)", name);
+ }
+ else if (sym_name_with_equal_p(name, len)) {
+ fprintf(fp, "MRB_SYM_E(%.*s)", (int)(len-1), name);
+ }
+ else if (sym_name_with_question_mark_p(name, len)) {
+ fprintf(fp, "MRB_SYM_Q(%.*s)", (int)(len-1), name);
+ }
+ else if (sym_name_with_bang_p(name, len)) {
+ fprintf(fp, "MRB_SYM_B(%.*s)", (int)(len-1), name);
+ }
+ else if (sym_name_ivar_p(name, len)) {
+ fprintf(fp, "MRB_IVSYM(%s)", name+1);
+ }
+ else if (sym_name_cvar_p(name, len)) {
+ fprintf(fp, "MRB_CVSYM(%s)", name+2);
+ }
+ else if ((op_name = sym_operator_name(name, len))) {
+ fprintf(fp, "MRB_OPSYM(%s)", op_name);
+ }
+ else {
+ char buf[32];
+ mrb_value name_obj = mrb_str_new(mrb, name, len);
+ mrb_str_cat_lit(mrb, init_syms_code, " ");
+ mrb_str_cat_cstr(mrb, init_syms_code, var_name);
+ snprintf(buf, sizeof(buf), "[%d] = ", idx);
+ mrb_str_cat_cstr(mrb, init_syms_code, buf);
+ mrb_str_cat_lit(mrb, init_syms_code, "mrb_intern_lit(mrb, ");
+ mrb_str_cat_str(mrb, init_syms_code, mrb_str_dump(mrb, name_obj));
+ mrb_str_cat_lit(mrb, init_syms_code, ");\n");
+ fputs("0", fp);
+ }
+ fputs(", ", fp);
+ return MRB_DUMP_OK;
+}
+
+static int
+cdump_syms(mrb_state *mrb, const char *name, const char *key, int n, int syms_len, const mrb_sym *syms, mrb_value init_syms_code, FILE *fp)
+{
+ int ai = mrb_gc_arena_save(mrb);
+ mrb_int code_len = RSTRING_LEN(init_syms_code);
+ const char *var_name = sym_var_name(mrb, name, key, n);
+ fprintf(fp, "mrb_DEFINE_SYMS_VAR(%s, %d, (", var_name, syms_len);
+ for (int i=0; i<syms_len; i++) {
+ cdump_sym(mrb, syms[i], var_name, i, init_syms_code, fp);
+ }
+ fputs("), ", fp);
+ if (code_len == RSTRING_LEN(init_syms_code)) fputs("const", fp);
+ fputs(");\n", fp);
+ mrb_gc_arena_restore(mrb, ai);
+ return MRB_DUMP_OK;
+}
+
+//Handle the simple/common case of debug_info:
+// - 1 file associated with a single irep
+// - mrb_debug_line_ary format only
+static int
+simple_debug_info(mrb_irep_debug_info *info)
+{
+ if (!info || info->flen != 1) {
+ return 0;
+ }
+ return 1;
+}
+
+//Adds debug information to c-structs and
+//adds filenames in init_syms_code block
+static int
+cdump_debug(mrb_state *mrb, const char *name, int n, mrb_irep_debug_info *info,
+ mrb_value init_syms_code, FILE *fp)
+{
+ char buffer[256];
+ const char *filename;
+ mrb_int file_len;
+ int len, i;
+ const char *line_type = "mrb_debug_line_ary";
+
+ if (!simple_debug_info(info))
+ return MRB_DUMP_INVALID_IREP;
+
+ len = info->files[0]->line_entry_count;
+
+ filename = mrb_sym_name_len(mrb, info->files[0]->filename_sym, &file_len);
+ snprintf(buffer, sizeof(buffer), " %s_debug_file_%d.filename_sym = mrb_intern_lit(mrb,\"",
+ name, n);
+ mrb_str_cat_cstr(mrb, init_syms_code, buffer);
+ mrb_str_cat_cstr(mrb, init_syms_code, filename);
+ mrb_str_cat_cstr(mrb, init_syms_code, "\");\n");
+
+ switch (info->files[0]->line_type) {
+ case mrb_debug_line_ary:
+ fprintf(fp, "static uint16_t %s_debug_lines_%d[%d] = {", name, n, len);
+ for (i=0; i<len; i++) {
+ if (i%10 == 0) fputs("\n", fp);
+ fprintf(fp, "0x%04x,", info->files[0]->lines.ary[i]);
+ }
+ fputs("};\n", fp);
+ break;
+
+ case mrb_debug_line_flat_map:
+ line_type = "mrb_debug_line_flat_map";
+ fprintf(fp, "static struct mrb_irep_debug_info_line %s_debug_lines_%d[%d] = {", name, n, len);
+ for (i=0; i<len; i++) {
+ mrb_irep_debug_info_line *fmap = &info->files[0]->lines.flat_map[i];
+ fprintf(fp, "\t{.start_pos=0x%04x,.line=%d},\n", fmap->start_pos, fmap->line);
+ }
+ fputs("};\n", fp);
+ break;
+
+ case mrb_debug_line_packed_map:
+ line_type = "mrb_debug_line_packed_map";
+ fprintf(fp, "static char %s_debug_lines_%d[] = \"", name, n);
+ uint8_t *pmap = info->files[0]->lines.packed_map;
+ for (i=0; i<len; i++) {
+ fprintf(fp, "\\x%02x", pmap[i]&0xff);
+ }
+ fputs("\";\n", fp);
+ break;
+ }
+ fprintf(fp, "static mrb_irep_debug_info_file %s_debug_file_%d = {\n", name, n);
+ fprintf(fp, "%d, %d, %d, %s, {%s_debug_lines_%d}};\n",
+ info->files[0]->start_pos,
+ info->files[0]->filename_sym,
+ info->files[0]->line_entry_count,
+ line_type,
+ name,n);
+ fprintf(fp, "static mrb_irep_debug_info_file *%s_debug_file_%d_ = &%s_debug_file_%d;\n", name, n, name, n);
+
+ fprintf(fp, "static mrb_irep_debug_info %s_debug_%d = {\n", name, n);
+ fprintf(fp, "%d, %d, &%s_debug_file_%d_};\n", info->pc_count, info->flen, name, n);
+
+ return MRB_DUMP_OK;
+}
+
+static int
+cdump_irep_struct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *name, int n, mrb_value init_syms_code, int *mp)
+{
+ int i, len;
+ int max = *mp;
+ int debug_available = 0;
+
+ /* dump reps */
+ if (irep->reps) {
+ for (i=0,len=irep->rlen; i<len; i++) {
+ *mp += len;
+ if (cdump_irep_struct(mrb, irep->reps[i], flags, fp, name, max+i, init_syms_code, mp) != MRB_DUMP_OK)
+ return MRB_DUMP_INVALID_ARGUMENT;
+ }
+ fprintf(fp, "static const mrb_irep *%s_reps_%d[%d] = {\n", name, n, len);
+ for (i=0,len=irep->rlen; i<len; i++) {
+ fprintf(fp, " &%s_irep_%d,\n", name, max+i);
+ }
+ fputs("};\n", fp);
+ }
+ /* dump pool */
+ if (irep->pool) {
+ len=irep->plen;
+ fprintf(fp, "static const mrb_pool_value %s_pool_%d[%d] = {\n", name, n, len);
+ for (i=0; i<len; i++) {
+ if (cdump_pool(mrb, &irep->pool[i], fp) != MRB_DUMP_OK)
+ return MRB_DUMP_INVALID_ARGUMENT;
+ }
+ fputs("};\n", fp);
+ }
+ /* dump syms */
+ if (irep->syms) {
+ cdump_syms(mrb, name, "syms", n, irep->slen, irep->syms, init_syms_code, fp);
+ }
+ /* dump iseq */
+ len=irep->ilen+sizeof(struct mrb_irep_catch_handler)*irep->clen;
+ fprintf(fp, "static const mrb_code %s_iseq_%d[%d] = {", name, n, len);
+ for (i=0; i<len; i++) {
+ if (i%20 == 0) fputs("\n", fp);
+ fprintf(fp, "0x%02x,", irep->iseq[i]);
+ }
+ fputs("};\n", fp);
+ /* dump lv */
+ if (irep->lv) {
+ cdump_syms(mrb, name, "lv", n, irep->nlocals-1, irep->lv, init_syms_code, fp);
+ }
+ /* dump debug */
+ if (flags & MRB_DUMP_DEBUG_INFO) {
+ if(cdump_debug(mrb, name, n, irep->debug_info,
+ init_syms_code, fp) == MRB_DUMP_OK) {
+ debug_available = 1;
+ }
+ }
+
+
+ /* dump irep */
+ fprintf(fp, "static const mrb_irep %s_irep_%d = {\n", name, n);
+ fprintf(fp, " %d,%d,%d,\n", irep->nlocals, irep->nregs, irep->clen);
+ fprintf(fp, " MRB_IREP_STATIC,%s_iseq_%d,\n", name, n);
+ if (irep->pool) {
+ fprintf(fp, " %s_pool_%d,", name, n);
+ }
+ else {
+ fputs( " NULL,", fp);
+ }
+ if (irep->syms) {
+ fprintf(fp, "%s_syms_%d,", name, n);
+ }
+ else {
+ fputs( "NULL,", fp);
+ }
+ if (irep->reps) {
+ fprintf(fp, "%s_reps_%d,\n", name, n);
+ }
+ else {
+ fputs( "NULL,\n", fp);
+ }
+ if (irep->lv) {
+ fprintf(fp, " %s_lv_%d,\n", name, n);
+ }
+ else {
+ fputs( " NULL,\t\t\t\t\t/* lv */\n", fp);
+ }
+ if(debug_available) {
+ fprintf(fp, " &%s_debug_%d,\n", name, n);
+ }
+ else {
+ fputs(" NULL,\t\t\t\t\t/* debug_info */\n", fp);
+ }
+ fprintf(fp, " %d,%d,%d,%d,0\n};\n", irep->ilen, irep->plen, irep->slen, irep->rlen);
+
+ return MRB_DUMP_OK;
+}
+
+int
+mrb_dump_irep_cstruct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname)
+{
+ if (fp == NULL || initname == NULL || initname[0] == '\0') {
+ return MRB_DUMP_INVALID_ARGUMENT;
+ }
+ if (fprintf(fp, "#include <mruby.h>\n"
+ "#include <mruby/irep.h>\n"
+ "#include <mruby/debug.h>\n"
+ "#include <mruby/proc.h>\n"
+ "#include <mruby/presym.h>\n"
+ "\n") < 0) {
+ return MRB_DUMP_WRITE_FAULT;
+ }
+ fputs("#define mrb_BRACED(...) {__VA_ARGS__}\n", fp);
+ fputs("#define mrb_DEFINE_SYMS_VAR(name, len, syms, qualifier) \\\n", fp);
+ fputs(" static qualifier mrb_sym name[len] = mrb_BRACED syms\n", fp);
+ fputs("\n", fp);
+ mrb_value init_syms_code = mrb_str_new_capa(mrb, 0);
+ int max = 1;
+ int n = cdump_irep_struct(mrb, irep, flags, fp, initname, 0, init_syms_code, &max);
+ if (n != MRB_DUMP_OK) return n;
+ fprintf(fp,
+ "%s\n"
+ "const struct RProc %s[] = {{\n",
+ (flags & MRB_DUMP_STATIC) ? "static"
+ : "#ifdef __cplusplus\n"
+ "extern\n"
+ "#endif",
+ initname);
+ fprintf(fp, "NULL,NULL,MRB_TT_PROC,MRB_GC_RED,0,{&%s_irep_0},NULL,{NULL},\n}};\n", initname);
+ fputs("static void\n", fp);
+ fprintf(fp, "%s_init_syms(mrb_state *mrb)\n", initname);
+ fputs("{\n", fp);
+ fputs(RSTRING_PTR(init_syms_code), fp);
+ fputs("}\n", fp);
+ return MRB_DUMP_OK;
+}
+#endif
diff --git a/src/class.c b/src/class.c
index 50ab0ea59..33a610c26 100644
--- a/src/class.c
+++ b/src/class.c
@@ -7,6 +7,7 @@
#include <stdarg.h>
#include <mruby.h>
#include <mruby/array.h>
+#include <mruby/hash.h>
#include <mruby/class.h>
#include <mruby/numeric.h>
#include <mruby/proc.h>
@@ -15,48 +16,285 @@
#include <mruby/error.h>
#include <mruby/data.h>
#include <mruby/istruct.h>
+#include <mruby/opcode.h>
+#include <mruby/presym.h>
-KHASH_DEFINE(mt, mrb_sym, mrb_method_t, TRUE, kh_int_hash_func, kh_int_hash_equal)
+union mt_ptr {
+ struct RProc *proc;
+ mrb_func_t func;
+};
+
+struct mt_elem {
+ union mt_ptr ptr;
+ size_t func_p:1;
+ size_t noarg_p:1;
+ mrb_sym key:sizeof(mrb_sym)*8-2;
+};
+
+/* method table structure */
+typedef struct mt_tbl {
+ size_t size;
+ size_t alloc;
+ struct mt_elem *table;
+} mt_tbl;
+
+/* Creates the method table. */
+static mt_tbl*
+mt_new(mrb_state *mrb)
+{
+ mt_tbl *t;
+
+ t = (mt_tbl*)mrb_malloc(mrb, sizeof(mt_tbl));
+ t->size = 0;
+ t->alloc = 0;
+ t->table = NULL;
+
+ return t;
+}
+
+static struct mt_elem *mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, size_t func_p, size_t noarg_p, union mt_ptr ptr);
+
+static void
+mt_rehash(mrb_state *mrb, mt_tbl *t)
+{
+ size_t old_alloc = t->alloc;
+ size_t new_alloc = old_alloc+1;
+ struct mt_elem *old_table = t->table;
+
+ khash_power2(new_alloc);
+ if (old_alloc == new_alloc) return;
+
+ t->alloc = new_alloc;
+ t->size = 0;
+ t->table = (struct mt_elem*)mrb_calloc(mrb, sizeof(struct mt_elem), new_alloc);
+
+ for (size_t i = 0; i < old_alloc; i++) {
+ struct mt_elem *slot = &old_table[i];
+
+ /* key = 0 means empty or deleted */
+ if (slot->key != 0) {
+ mt_put(mrb, t, slot->key, slot->func_p, slot->noarg_p, slot->ptr);
+ }
+ }
+ mrb_free(mrb, old_table);
+}
+
+#define slot_empty_p(slot) ((slot)->key == 0 && (slot)->func_p == 0)
+
+/* Set the value for the symbol in the method table. */
+static struct mt_elem*
+mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, size_t func_p, size_t noarg_p, union mt_ptr ptr)
+{
+ size_t hash, pos, start;
+ struct mt_elem *dslot = NULL;
+
+ if (t->alloc == 0) {
+ mt_rehash(mrb, t);
+ }
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct mt_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ slot->func_p = func_p;
+ slot->noarg_p = noarg_p;
+ slot->ptr = ptr;
+ return slot;
+ }
+ else if (slot->key == 0) { /* empty or deleted */
+ if (slot->func_p == 0) { /* empty */
+ t->size++;
+ slot->key = sym;
+ slot->func_p = func_p;
+ slot->noarg_p = noarg_p;
+ slot->ptr = ptr;
+ return slot;
+ }
+ else if (!dslot) { /* deleted */
+ dslot = slot;
+ }
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ if (dslot) {
+ t->size++;
+ dslot->key = sym;
+ dslot->func_p = func_p;
+ dslot->noarg_p = noarg_p;
+ dslot->ptr = ptr;
+ return dslot;
+ }
+ /* no room */
+ mt_rehash(mrb, t);
+ start = pos = hash & (t->alloc-1);
+ }
+ }
+}
+
+/* Get a value for a symbol from the method table. */
+static struct mt_elem*
+mt_get(mrb_state *mrb, mt_tbl *t, mrb_sym sym)
+{
+ size_t hash, pos, start;
+
+ if (t == NULL) return NULL;
+ if (t->alloc == 0) return NULL;
+ if (t->size == 0) return NULL;
+
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct mt_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ return slot;
+ }
+ else if (slot_empty_p(slot)) {
+ return NULL;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return NULL;
+ }
+ }
+}
+
+/* Deletes the value for the symbol from the method table. */
+static mrb_bool
+mt_del(mrb_state *mrb, mt_tbl *t, mrb_sym sym)
+{
+ size_t hash, pos, start;
+
+ if (t == NULL) return FALSE;
+ if (t->alloc == 0) return FALSE;
+ if (t->size == 0) return FALSE;
+
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct mt_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ t->size--;
+ slot->key = 0;
+ slot->func_p = 1;
+ return TRUE;
+ }
+ else if (slot_empty_p(slot)) {
+ return FALSE;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return FALSE;
+ }
+ }
+}
+
+/* Copy the method table. */
+static struct mt_tbl*
+mt_copy(mrb_state *mrb, mt_tbl *t)
+{
+ mt_tbl *t2;
+ size_t i;
+
+ if (t == NULL) return NULL;
+ if (t->alloc == 0) return NULL;
+ if (t->size == 0) return NULL;
+
+ t2 = mt_new(mrb);
+ for (i=0; i<t->alloc; i++) {
+ struct mt_elem *slot = &t->table[i];
+
+ if (slot->key) {
+ mt_put(mrb, t2, slot->key, slot->func_p, slot->noarg_p, slot->ptr);
+ }
+ }
+ return t2;
+}
+
+/* Free memory of the method table. */
+static void
+mt_free(mrb_state *mrb, mt_tbl *t)
+{
+ mrb_free(mrb, t->table);
+ mrb_free(mrb, t);
+}
+
+MRB_API void
+mrb_mt_foreach(mrb_state *mrb, struct RClass *c, mrb_mt_foreach_func *fn, void *p)
+{
+ mt_tbl *t = c->mt;
+ size_t i;
+
+ if (t == NULL) return;
+ if (t->alloc == 0) return;
+ if (t->size == 0) return;
+
+ for (i=0; i<t->alloc; i++) {
+ struct mt_elem *slot = &t->table[i];
+
+ if (slot->key) {
+ mrb_method_t m;
+
+ if (slot->func_p) {
+ MRB_METHOD_FROM_FUNC(m, slot->ptr.func);
+ }
+ else {
+ MRB_METHOD_FROM_PROC(m, slot->ptr.proc);
+ }
+ if (slot->noarg_p) {
+ MRB_METHOD_NOARG_SET(m);
+ }
+
+ if (fn(mrb, slot->key, m, p) != 0)
+ return;
+ }
+ }
+ return;
+}
void
mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c)
{
- khiter_t k;
- khash_t(mt) *h = c->mt;
+ mt_tbl *t = c->mt;
+ size_t i;
- 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 (t == NULL) return;
+ if (t->alloc == 0) return;
+ if (t->size == 0) return;
- if (MRB_METHOD_PROC_P(m)) {
- struct RProc *p = MRB_METHOD_PROC(m);
- mrb_gc_mark(mrb, (struct RBasic*)p);
- }
+ for (i=0; i<t->alloc; i++) {
+ struct mt_elem *slot = &t->table[i];
+
+ if (slot->key && !slot->func_p) { /* Proc pointer */
+ struct RProc *p = slot->ptr.proc;
+ mrb_gc_mark(mrb, (struct RBasic*)p);
}
}
+ return;
}
size_t
mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c)
{
- khash_t(mt) *h = c->mt;
+ struct mt_tbl *h = c->mt;
if (!h) return 0;
- return kh_size(h);
+ return h->size;
}
void
mrb_gc_free_mt(mrb_state *mrb, struct RClass *c)
{
- kh_destroy(mt, mrb, c->mt);
+ if (c->mt) mt_free(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__");
+ mrb_sym nsym = MRB_SYM(__classname__);
if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return;
if (outer == NULL || outer == mrb->object_class) {
@@ -66,15 +304,26 @@ mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb
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));
+ mrb_obj_iv_set_force(mrb, (struct RObject*)c, MRB_SYM(__outer__),
+ mrb_obj_value(outer));
}
return;
}
- mrb_str_cat_cstr(mrb, name, "::");
- mrb_str_cat_cstr(mrb, name, mrb_sym2name(mrb, id));
+ else {
+ mrb_int len;
+ const char *n = mrb_sym_name_len(mrb, id, &len);
+
+ mrb_str_cat_lit(mrb, name, "::");
+ mrb_str_cat(mrb, name, n, len);
+ }
}
- mrb_obj_iv_set(mrb, (struct RObject*)c, nsym, name);
+ mrb_obj_iv_set_force(mrb, (struct RObject*)c, nsym, name);
+}
+
+mrb_bool
+mrb_const_name_p(mrb_state *mrb, const char *name, mrb_int len)
+{
+ return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1);
}
static void
@@ -92,9 +341,9 @@ 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 = MRB_OBJ_ALLOC(mrb, MRB_TT_SCLASS, mrb->class_class);
sc->flags |= MRB_FL_CLASS_IS_INHERITED;
- sc->mt = kh_init(mt, mrb);
+ sc->mt = mt_new(mrb);
sc->iv = 0;
if (o->tt == MRB_TT_CLASS) {
c = (struct RClass*)o;
@@ -119,7 +368,21 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
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));
+ mrb_obj_iv_set(mrb, (struct RObject*)sc, MRB_SYM(__attached__), mrb_obj_value(o));
+ sc->flags |= o->flags & MRB_FL_OBJ_IS_FROZEN;
+}
+
+static mrb_value
+class_name_str(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, "#<Module:") :
+ mrb_str_new_lit(mrb, "#<Class:");
+ mrb_str_cat_str(mrb, path, mrb_ptr_to_str(mrb, c));
+ mrb_str_cat_lit(mrb, path, ">");
+ }
+ return path;
}
static struct RClass*
@@ -157,7 +420,7 @@ 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));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class/module", obj);
}
}
@@ -187,15 +450,15 @@ 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*
+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));
+ if (!mrb_module_p(old)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a module", old);
}
return mrb_class_ptr(old);
}
@@ -203,6 +466,15 @@ mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
}
MRB_API struct RClass*
+mrb_define_module_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ struct RClass * c = define_module(mrb, name, outer);
+
+ setup_class(mrb, outer, c, name);
+ return c;
+}
+
+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);
@@ -228,9 +500,8 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *
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));
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %n (%C not %C)",
+ name, c->super, super);
}
return c;
}
@@ -245,7 +516,7 @@ MRB_API struct RClass*
mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super)
{
if (!super) {
- mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name));
+ mrb_warn(mrb, "no super class for '%n', Object assumed", name);
}
return define_class(mrb, name, super, mrb->object_class);
}
@@ -257,14 +528,10 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *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);
+#ifndef MRB_NO_METHOD_CACHE
+static void mc_clear(mrb_state *mrb);
#else
-#define mc_clear_all(mrb)
-#define mc_clear_by_class(mrb,c)
-#define mc_clear_by_id(mrb,c,s)
+#define mc_clear(mrb)
#endif
static void
@@ -277,24 +544,23 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
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");
+ mrb_mc_clear_by_class(mrb, klass);
+ mid = MRB_SYM(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*
+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));
+ if (!mrb_class_p(super)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%!v given)", super);
}
s = mrb_class_ptr(super);
}
@@ -305,14 +571,14 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
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));
+ if (!mrb_class_p(old)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class", 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);
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %v", old);
}
}
return c;
@@ -326,21 +592,29 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
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_sym sym = mrb_intern_check_cstr(mrb, name);
+ if (!sym) return FALSE;
+ return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), sym);
+}
+
+MRB_API mrb_bool
+mrb_class_defined_id(mrb_state *mrb, mrb_sym name)
+{
+ return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), name);
}
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_sym sym = mrb_intern_check_cstr(mrb, name);
+ if (!sym) return FALSE;
+ return mrb_const_defined_at(mrb, mrb_obj_value(outer), sym);
+}
+
+MRB_API mrb_bool
+mrb_class_defined_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ return mrb_const_defined_at(mrb, mrb_obj_value(outer), name);
}
MRB_API struct RClass*
@@ -350,19 +624,30 @@ mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
}
MRB_API struct RClass*
+mrb_class_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ return class_from_sym(mrb, outer, 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)
+mrb_class_get_id(mrb_state *mrb, mrb_sym name)
+{
+ return mrb_class_get_under_id(mrb, mrb->object_class, name);
+}
+
+MRB_API struct RClass*
+mrb_exc_get_id(mrb_state *mrb, mrb_sym name)
{
struct RClass *exc, *e;
- mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class),
- mrb_intern_cstr(mrb, name));
+ mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), name);
- if (mrb_type(c) != MRB_TT_CLASS) {
+ if (!mrb_class_p(c)) {
mrb_raise(mrb, mrb->eException_class, "exception corrupted");
}
exc = e = mrb_class_ptr(c);
@@ -382,15 +667,27 @@ mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
}
MRB_API struct RClass*
+mrb_module_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name)
+{
+ return module_from_sym(mrb, outer, name);
+}
+
+MRB_API struct RClass*
mrb_module_get(mrb_state *mrb, const char *name)
{
return mrb_module_get_under(mrb, mrb->object_class, name);
}
+MRB_API struct RClass*
+mrb_module_get_id(mrb_state *mrb, mrb_sym name)
+{
+ return mrb_module_get_under_id(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 name 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
@@ -404,50 +701,60 @@ mrb_module_get(mrb_state *mrb, const char *name)
* \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_define_class_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym 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));
+ mrb_warn(mrb, "no super class for '%C::%n', Object assumed", outer, id);
}
#endif
- c = define_class(mrb, id, super, outer);
- setup_class(mrb, outer, c, id);
+ c = define_class(mrb, name, super, outer);
+ setup_class(mrb, outer, c, name);
return c;
}
+MRB_API struct RClass*
+mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super)
+{
+ return mrb_define_class_under_id(mrb, outer, mrb_intern_cstr(mrb, name), super);
+}
+
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;
+ mt_tbl *h;
+ union mt_ptr ptr;
+
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)) {
+ mrb_check_frozen(mrb, c);
+ if (!h) h = c->mt = mt_new(mrb);
+ if (MRB_METHOD_PROC_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);
+ ptr.proc = p;
+ if (p) {
+ if (p->color != MRB_GC_RED) {
+ 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);
+ }
+ }
+ else {
+ mrb_assert(MRB_FROZEN_P(p) && MRB_PROC_SCOPE_P(p));
+ mrb_assert(p->c == NULL && p->upper == NULL && p->e.target_class == NULL);
+ }
}
}
- mc_clear_by_id(mrb, c, mid);
+ else {
+ ptr.func = MRB_METHOD_FUNC(m);
+ }
+ mt_put(mrb, h, mid, MRB_METHOD_FUNC_P(m), MRB_METHOD_NOARG_P(m), ptr);
+ mc_clear(mrb);
}
MRB_API void
@@ -457,6 +764,12 @@ mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t f
int ai = mrb_gc_arena_save(mrb);
MRB_METHOD_FROM_FUNC(m, func);
+#ifndef MRB_USE_METHOD_T_STRUCT
+ mrb_assert(MRB_METHOD_FUNC(m) == func);
+#endif
+ if (aspec == MRB_ARGS_NONE()) {
+ MRB_METHOD_NOARG_SET(m);
+ }
mrb_define_method_raw(mrb, c, mid, m);
mrb_gc_arena_restore(mrb, ai);
}
@@ -471,15 +784,10 @@ mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t
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));
+ mrb_raisef(mrb, E_NOTIMP_ERROR, "%n() function is unimplemented on this machine", ci->mid);
}
}
@@ -493,33 +801,17 @@ mrb_notimplement_m(mrb_state *mrb, mrb_value self)
}
static mrb_value
-check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m)
-{
- mrb_value tmp;
-
- tmp = mrb_check_convert_type(mrb, val, t, c, m);
- if (mrb_nil_p(tmp)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c));
- }
- return tmp;
-}
-
-static mrb_value
-to_str(mrb_state *mrb, mrb_value val)
-{
- return check_type(mrb, val, MRB_TT_STRING, "String", "to_str");
-}
-
-static mrb_value
to_ary(mrb_state *mrb, mrb_value val)
{
- return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+ mrb_check_type(mrb, val, MRB_TT_ARRAY);
+ return val;
}
static mrb_value
to_hash(mrb_state *mrb, mrb_value val)
{
- return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash");
+ mrb_check_type(mrb, val, MRB_TT_HASH);
+ return val;
}
#define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss)
@@ -530,29 +822,56 @@ 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]);
+ struct RArray *a = mrb_ary_ptr(mrb->c->ci->stack[1]);
argc = ARY_LEN(a);
}
return argc;
}
-MRB_API mrb_value*
+MRB_API const mrb_value*
mrb_get_argv(mrb_state *mrb)
{
mrb_int argc = mrb->c->ci->argc;
- mrb_value *array_argv;
+ mrb_value *array_argv = mrb->c->ci->stack + 1;
if (argc < 0) {
- struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]);
+ struct RArray *a = mrb_ary_ptr(*array_argv);
array_argv = ARY_PTR(a);
}
- else {
- array_argv = NULL;
- }
return array_argv;
}
+MRB_API mrb_value
+mrb_get_arg1(mrb_state *mrb)
+{
+ mrb_int argc = mrb->c->ci->argc;
+ mrb_value *array_argv = mrb->c->ci->stack + 1;
+ if (argc < 0) {
+ struct RArray *a = mrb_ary_ptr(*array_argv);
+
+ argc = ARY_LEN(a);
+ array_argv = ARY_PTR(a);
+ }
+ if (argc != 1) {
+ mrb_argnum_error(mrb, argc, 1, 1);
+ }
+ return array_argv[0];
+}
+
+MRB_API mrb_bool
+mrb_block_given_p(mrb_state *mrb)
+{
+ const mrb_callinfo *ci = mrb->c->ci;
+ int argc = ci->argc;
+ int idx = (argc < 0) ? 2 : argc + 1;
+ mrb_value b = ci->stack[idx];
+
+ return !mrb_nil_p(b);
+}
+
+void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
+
/*
retrieve arguments from mrb_state.
@@ -565,23 +884,25 @@ mrb_get_argv(mrb_state *mrb)
string mruby type C type note
----------------------------------------------------------------------------------------------
o: Object [mrb_value]
- C: class/module [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
+ s: String [const char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil
+ z: String [const char*] NUL terminated string; z! gives NULL for nil
+ a: Array [const mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil
+ c: Class/Module [strcut RClass*]
+ f: Integer/Float [mrb_float]
+ i: Integer/Float [mrb_int]
+ b: boolean [mrb_bool]
+ n: String/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; when ! follows, the value may be nil
+ I: inline struct [void*]
+ &: block [mrb_value] &! raises exception if no block given
+ *: rest argument [const 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
+ ':': keyword args [mrb_kwargs const] Get keyword arguments
*/
MRB_API mrb_int
mrb_get_args(mrb_state *mrb, const char *format, ...)
@@ -590,17 +911,24 @@ mrb_get_args(mrb_state *mrb, const char *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_int argc = mrb->c->ci->argc;
+ mrb_value *array_argv = mrb->c->ci->stack+1;
+ mrb_bool argv_on_stack = argc >= 0;
mrb_bool opt = FALSE;
mrb_bool opt_skip = TRUE;
mrb_bool given = TRUE;
+ mrb_value kdict;
+ mrb_bool reqkarg = FALSE;
+ int argc_min = 0, argc_max = 0;
+ if (!argv_on_stack) {
+ struct RArray *a = mrb_ary_ptr(*array_argv);
+ array_argv = ARY_PTR(a);
+ argc = ARY_LEN(a);
+ }
va_start(ap, format);
-#define ARGV \
- (array_argv ? array_argv : (mrb->c->stack + 1))
+#define ARGV array_argv
while ((c = *fmt++)) {
switch (c) {
@@ -609,23 +937,41 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
break;
case '*':
opt_skip = FALSE;
+ argc_max = -1;
+ if (!reqkarg) reqkarg = strchr(fmt, ':') ? TRUE : FALSE;
goto check_exit;
case '!':
break;
+ case ':':
+ reqkarg = TRUE;
+ /* fall through */
case '&': case '?':
if (opt) opt_skip = FALSE;
break;
default:
+ if (!opt) argc_min++;
+ argc_max++;
break;
}
}
check_exit:
+ if (reqkarg && argc > argc_min && mrb_hash_p(kdict = ARGV[argc - 1])) {
+ mrb_hash_check_kdict(mrb, kdict);
+ argc --;
+ }
+ else {
+ kdict = mrb_nil_value();
+ }
+
opt = FALSE;
i = 0;
while ((c = *format++)) {
+ mrb_value *argv = ARGV;
+ mrb_bool altmode;
+
switch (c) {
- case '|': case '*': case '&': case '?':
+ case '|': case '*': case '&': case '?': case ':':
break;
default:
if (argc <= i) {
@@ -633,12 +979,20 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
given = FALSE;
}
else {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
+ mrb_argnum_error(mrb, argc, argc_min, argc_max);
}
}
break;
}
+ if (*format == '!') {
+ format ++;
+ altmode = TRUE;
+ }
+ else {
+ altmode = FALSE;
+ }
+
switch (c) {
case 'o':
{
@@ -646,8 +1000,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_value*);
if (i < argc) {
- *p = ARGV[arg_i++];
- i++;
+ *p = argv[i++];
}
}
break;
@@ -659,12 +1012,27 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
if (i < argc) {
mrb_value ss;
- ss = ARGV[arg_i++];
+ ss = argv[i++];
if (!class_ptr_p(ss)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", ss);
}
*p = ss;
- i++;
+ }
+ }
+ break;
+ case 'c':
+ {
+ struct RClass **p;
+
+ p = va_arg(ap, struct RClass**);
+ if (i < argc) {
+ mrb_value ss;
+
+ ss = argv[i++];
+ if (!class_ptr_p(ss)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", ss);
+ }
+ *p = mrb_class_ptr(ss);
}
}
break;
@@ -673,17 +1041,11 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_value *p;
p = va_arg(ap, mrb_value*);
- if (*format == '!') {
- format++;
- if (i < argc && mrb_nil_p(ARGV[arg_i])) {
- *p = ARGV[arg_i++];
- i++;
- break;
- }
- }
if (i < argc) {
- *p = to_str(mrb, ARGV[arg_i++]);
- i++;
+ *p = argv[i++];
+ if (!(altmode && mrb_nil_p(*p))) {
+ mrb_to_str(mrb, *p);
+ }
}
}
break;
@@ -692,17 +1054,11 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_value *p;
p = va_arg(ap, mrb_value*);
- if (*format == '!') {
- format++;
- if (i < argc && mrb_nil_p(ARGV[arg_i])) {
- *p = ARGV[arg_i++];
- i++;
- break;
- }
- }
if (i < argc) {
- *p = to_ary(mrb, ARGV[arg_i++]);
- i++;
+ *p = argv[i++];
+ if (!(altmode && mrb_nil_p(*p))) {
+ *p = to_ary(mrb, *p);
+ }
}
}
break;
@@ -711,42 +1067,33 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_value *p;
p = va_arg(ap, mrb_value*);
- if (*format == '!') {
- format++;
- if (i < argc && mrb_nil_p(ARGV[arg_i])) {
- *p = ARGV[arg_i++];
- i++;
- break;
- }
- }
if (i < argc) {
- *p = to_hash(mrb, ARGV[arg_i++]);
- i++;
+ *p = argv[i++];
+ if (!(altmode && mrb_nil_p(*p))) {
+ *p = to_hash(mrb, *p);
+ }
}
}
break;
case 's':
{
mrb_value ss;
- char **ps = 0;
+ const char **ps = 0;
mrb_int *pl = 0;
- ps = va_arg(ap, char**);
+ ps = va_arg(ap, const char**);
pl = va_arg(ap, mrb_int*);
- if (*format == '!') {
- format++;
- if (i < argc && mrb_nil_p(ARGV[arg_i])) {
+ if (i < argc) {
+ ss = argv[i++];
+ if (altmode && mrb_nil_p(ss)) {
*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++;
+ else {
+ mrb_to_str(mrb, ss);
+ *ps = RSTRING_PTR(ss);
+ *pl = RSTRING_LEN(ss);
+ }
}
}
break;
@@ -756,18 +1103,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
const char **ps;
ps = va_arg(ap, const char**);
- if (*format == '!') {
- format++;
- if (i < argc && mrb_nil_p(ARGV[arg_i])) {
+ if (i < argc) {
+ ss = argv[i++];
+ if (altmode && mrb_nil_p(ss)) {
*ps = NULL;
- i++; arg_i++;
- break;
}
- }
- if (i < argc) {
- ss = to_str(mrb, ARGV[arg_i++]);
- *ps = mrb_string_value_cstr(mrb, &ss);
- i++;
+ else {
+ mrb_to_str(mrb, ss);
+ *ps = RSTRING_CSTR(mrb, ss);
+ }
}
}
break;
@@ -775,26 +1119,23 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
{
mrb_value aa;
struct RArray *a;
- mrb_value **pb;
+ const mrb_value **pb;
mrb_int *pl;
- pb = va_arg(ap, mrb_value**);
+ pb = va_arg(ap, const mrb_value**);
pl = va_arg(ap, mrb_int*);
- if (*format == '!') {
- format++;
- if (i < argc && mrb_nil_p(ARGV[arg_i])) {
+ if (i < argc) {
+ aa = argv[i++];
+ if (altmode && mrb_nil_p(aa)) {
*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++;
+ else {
+ aa = to_ary(mrb, aa);
+ a = mrb_ary_ptr(aa);
+ *pb = ARY_PTR(a);
+ *pl = ARY_LEN(a);
+ }
}
}
break;
@@ -805,27 +1146,23 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, void**);
if (i < argc) {
- ss = ARGV[arg_i];
- if (mrb_type(ss) != MRB_TT_ISTRUCT)
+ ss = argv[i++];
+ if (!mrb_istruct_p(ss))
{
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not inline struct", ss);
}
*p = mrb_istruct_ptr(ss);
- arg_i++;
- i++;
}
}
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_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++;
+ *p = mrb_as_float(mrb, argv[i]); i++;
}
}
break;
@@ -836,31 +1173,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
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++;
+ *p = mrb_as_int(mrb, argv[i]); i++;
}
}
break;
@@ -869,9 +1182,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_bool *boolp = va_arg(ap, mrb_bool*);
if (i < argc) {
- mrb_value b = ARGV[arg_i++];
+ mrb_value b = argv[i++];
*boolp = mrb_test(b);
- i++;
}
}
break;
@@ -883,9 +1195,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
if (i < argc) {
mrb_value ss;
- ss = ARGV[arg_i++];
+ ss = argv[i++];
*symp = to_sym(mrb, ss);
- i++;
}
}
break;
@@ -896,17 +1207,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
datap = va_arg(ap, void**);
type = va_arg(ap, struct mrb_data_type const*);
- if (*format == '!') {
- format++;
- if (i < argc && mrb_nil_p(ARGV[arg_i])) {
+ if (i < argc) {
+ mrb_value dd = argv[i++];
+ if (altmode && mrb_nil_p(dd)) {
*datap = 0;
- i++; arg_i++;
- break;
}
- }
- if (i < argc) {
- *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type);
- ++i;
+ else {
+ *datap = mrb_data_get_ptr(mrb, dd, type);
+ }
}
}
break;
@@ -917,22 +1225,19 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_value*);
if (mrb->c->ci->argc < 0) {
- bp = mrb->c->stack + 2;
+ bp = mrb->c->ci->stack + 2;
}
else {
- bp = mrb->c->stack + mrb->c->ci->argc + 1;
+ bp = mrb->c->ci->stack + mrb->c->ci->argc + 1;
}
- if (*format == '!') {
- format ++;
- if (mrb_nil_p(*bp)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
- }
+ if (altmode && mrb_nil_p(*bp)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
*p = *bp;
}
break;
case '|':
- if (opt_skip && i == argc) return argc;
+ if (opt_skip && i == argc) goto finish;
opt = TRUE;
break;
case '?':
@@ -946,30 +1251,25 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
case '*':
{
- mrb_value **var;
+ const mrb_value **var;
mrb_int *pl;
- mrb_bool nocopy = array_argv ? TRUE : FALSE;
+ mrb_bool nocopy = (altmode || !argv_on_stack) ? TRUE : FALSE;
- if (*format == '!') {
- format++;
- nocopy = TRUE;
- }
- var = va_arg(ap, mrb_value**);
+ var = va_arg(ap, const mrb_value**);
pl = va_arg(ap, mrb_int*);
if (argc > i) {
*pl = argc-i;
if (*pl > 0) {
if (nocopy) {
- *var = ARGV+arg_i;
+ *var = argv+i;
}
else {
- mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i);
+ mrb_value args = mrb_ary_new_from_values(mrb, *pl, argv+i);
RARRAY(args)->c = NULL;
*var = RARRAY_PTR(args);
}
}
i = argc;
- arg_i += *pl;
}
else {
*pl = 0;
@@ -977,8 +1277,64 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
break;
+
+ case ':':
+ {
+ mrb_value ksrc = mrb_hash_p(kdict) ? mrb_hash_dup(mrb, kdict) : mrb_hash_new(mrb);
+ const mrb_kwargs *kwargs = va_arg(ap, const mrb_kwargs*);
+ mrb_value *rest;
+
+ if (kwargs == NULL) {
+ rest = NULL;
+ }
+ else {
+ uint32_t kwnum = kwargs->num;
+ uint32_t required = kwargs->required;
+ const mrb_sym *kname = kwargs->table;
+ mrb_value *values = kwargs->values;
+ uint32_t j;
+ const uint32_t keyword_max = 40;
+
+ if (kwnum > keyword_max || required > kwnum) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword number is too large");
+ }
+
+ for (j = required; j > 0; j --, kname ++, values ++) {
+ mrb_value k = mrb_symbol_value(*kname);
+ if (!mrb_hash_key_p(mrb, ksrc, k)) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "missing keyword: %n", *kname);
+ }
+ *values = mrb_hash_delete_key(mrb, ksrc, k);
+ mrb_gc_protect(mrb, *values);
+ }
+
+ for (j = kwnum - required; j > 0; j --, kname ++, values ++) {
+ mrb_value k = mrb_symbol_value(*kname);
+ if (mrb_hash_key_p(mrb, ksrc, k)) {
+ *values = mrb_hash_delete_key(mrb, ksrc, k);
+ mrb_gc_protect(mrb, *values);
+ }
+ else {
+ *values = mrb_undef_value();
+ }
+ }
+
+ rest = kwargs->rest;
+ }
+
+ if (rest) {
+ *rest = ksrc;
+ }
+ else if (!mrb_hash_empty_p(mrb, ksrc)) {
+ ksrc = mrb_hash_keys(mrb, ksrc);
+ ksrc = RARRAY_PTR(ksrc)[0];
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown keyword: %v", ksrc);
+ }
+ }
+ break;
+
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %c", c);
break;
}
}
@@ -986,8 +1342,10 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
#undef ARGV
if (!c && argc > i) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
+ mrb_argnum_error(mrb, argc, argc_min, argc_max);
}
+
+finish:
va_end(ap);
return i;
}
@@ -997,7 +1355,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
{
struct RClass *c;
- c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class);
+ c = 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);
@@ -1005,7 +1363,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
else {
c->super = mrb->object_class;
}
- c->mt = kh_init(mt, mrb);
+ c->mt = mt_new(mrb);
return c;
}
@@ -1013,14 +1371,14 @@ static void
boot_initmod(mrb_state *mrb, struct RClass *mod)
{
if (!mod->mt) {
- mod->mt = kh_init(mt, mrb);
+ mod->mt = mt_new(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);
+ struct RClass *ic = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, mrb->class_class);
if (m->tt == MRB_TT_ICLASS) {
m = m->c;
}
@@ -1044,8 +1402,10 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
void *klass_mt = find_origin(c)->mt;
while (m) {
- int superclass_seen = 0;
+ int original_seen = FALSE;
+ int superclass_seen = FALSE;
+ if (c == ins_pos) original_seen = TRUE;
if (m->flags & MRB_FL_CLASS_IS_PREPENDED)
goto skip;
@@ -1054,16 +1414,17 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
p = c->super;
while (p) {
+ if (c == p) original_seen = TRUE;
if (p->tt == MRB_TT_ICLASS) {
if (p->mt == m->mt) {
- if (!superclass_seen) {
+ if (!superclass_seen && original_seen) {
ins_pos = p; /* move insert point */
}
goto skip;
}
} else if (p->tt == MRB_TT_CLASS) {
if (!search_super) break;
- superclass_seen = 1;
+ superclass_seen = TRUE;
}
p = p->super;
}
@@ -1072,65 +1433,123 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
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);
+ mc_clear(mrb);
return 0;
}
+static int
+fix_include_module(mrb_state *mrb, struct RBasic *obj, void *data)
+{
+ struct RClass **m = (struct RClass**)data;
+
+ if (obj->tt == MRB_TT_ICLASS && obj->c == m[0] && !MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) {
+ struct RClass *ic = (struct RClass*)obj;
+ include_module_at(mrb, ic, ic, m[1], 1);
+ }
+ return MRB_EACH_OBJ_OK;
+}
+
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_check_frozen(mrb, c);
+ if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
}
+ if (c->tt == MRB_TT_MODULE && (c->flags & MRB_FL_CLASS_IS_INHERITED)) {
+ struct RClass *data[2];
+ data[0] = c;
+ data[1] = m;
+ mrb_objspace_each_objects(mrb, fix_include_module, data);
+ }
+}
+
+static int
+fix_prepend_module(mrb_state *mrb, struct RBasic *obj, void *data)
+{
+ struct RClass **m = (struct RClass**)data;
+ struct RClass *c = (struct RClass*)obj;
+
+ if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
+ struct RClass *p = c->super;
+ struct RClass *ins_pos = c;
+ while (p) {
+ if (c == m[0]) break;
+ if (p == m[0]->super->c) {
+ ins_pos = c;
+ }
+ if (p->tt == MRB_TT_CLASS) break;
+ if (p->c == m[0]) {
+ include_module_at(mrb, ins_pos, ins_pos, m[1], 0);
+ break;
+ }
+ c = p;
+ p = p->super;
+ }
+ }
+ return MRB_EACH_OBJ_OK;
}
MRB_API void
mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
{
struct RClass *origin;
- int changed = 0;
+ mrb_check_frozen(mrb, c);
if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
- origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
+ struct RClass *c0;
+
+ if (c->tt == MRB_TT_ICLASS) {
+ c0 = c->c;
+ }
+ else {
+ c0 = c;
+ }
+ origin = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, c0);
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);
+ c->mt = NULL;
+ origin->iv = c->iv;
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) {
+ if (include_module_at(mrb, c, c, m, 0) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected");
}
+ if (c->tt == MRB_TT_MODULE &&
+ (c->flags & (MRB_FL_CLASS_IS_INHERITED|MRB_FL_CLASS_IS_PREPENDED))) {
+ struct RClass *data[2];
+ data[0] = c;
+ data[1] = m;
+ mrb_objspace_each_objects(mrb, fix_prepend_module, data);
+ }
}
static mrb_value
mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod)
{
- mrb_value klass;
+ struct RClass *c;
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));
+ mrb_get_args(mrb, "c", &c);
+ mrb_prepend_module(mrb, c, mrb_class_ptr(mod));
return mod;
}
static mrb_value
mrb_mod_append_features(mrb_state *mrb, mrb_value mod)
{
- mrb_value klass;
+ struct RClass *c;
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));
+ mrb_get_args(mrb, "c", &c);
+ mrb_include_module(mrb, c, mrb_class_ptr(mod));
return mod;
}
@@ -1193,10 +1612,9 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_mod_extend_object(mrb_state *mrb, mrb_value mod)
{
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
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;
}
@@ -1223,33 +1641,44 @@ 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)
+/* returns mrb_class_ptr(mrb_singleton_class()) */
+/* except that it return NULL for immediate values */
+MRB_API struct RClass*
+mrb_singleton_class_ptr(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);
+ return mrb->nil_class;
+ return mrb->false_class;
case MRB_TT_TRUE:
- return mrb_obj_value(mrb->true_class);
+ return 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_INTEGER:
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
#endif
- mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton");
- return mrb_nil_value(); /* not reached */
+ return NULL;
default:
break;
}
obj = mrb_basic_ptr(v);
prepare_singleton_class(mrb, obj);
- return mrb_obj_value(obj->c);
+ return obj->c;
+}
+
+MRB_API mrb_value
+mrb_singleton_class(mrb_state *mrb, mrb_value v)
+{
+ struct RClass *c = mrb_singleton_class_ptr(mrb, v);
+
+ if (c == NULL) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton");
+ }
+ return mrb_obj_value(c);
}
MRB_API void
@@ -1260,60 +1689,56 @@ mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name,
}
MRB_API void
+mrb_define_singleton_method_id(mrb_state *mrb, struct RObject *o, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
+{
+ prepare_singleton_class(mrb, (struct RBasic*)o);
+ mrb_define_method_id(mrb, o->c, 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_id(mrb_state *mrb, struct RClass *c, mrb_sym 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);
+ mrb_define_singleton_method_id(mrb, (struct RObject*)c, name, func, aspec);
}
-#ifdef MRB_METHOD_CACHE
-static void
-mc_clear_all(mrb_state *mrb)
+MRB_API void
+mrb_define_module_function_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec)
{
- struct mrb_cache_entry *mc = mrb->cache;
- int i;
+ mrb_define_class_method_id(mrb, c, name, func, aspec);
+ mrb_define_method_id(mrb, c, name, func, aspec);
+}
- for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
- mc[i].c = 0;
- }
+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_module_function_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
}
+#ifndef MRB_NO_METHOD_CACHE
static void
-mc_clear_by_class(mrb_state *mrb, struct RClass *c)
+mc_clear(mrb_state *mrb)
{
- struct mrb_cache_entry *mc = mrb->cache;
- 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; i<MRB_METHOD_CACHE_SIZE; i++) {
- if (mc[i].c == c) mc[i].c = 0;
- }
+ memset(mrb->cache, 0, MRB_METHOD_CACHE_SIZE*sizeof(mrb->cache[0]));
}
-static void
-mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid)
+void
+mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c)
{
struct mrb_cache_entry *mc = mrb->cache;
int i;
if (c->flags & MRB_FL_CLASS_IS_INHERITED) {
- mc_clear_all(mrb);
- c->flags &= ~MRB_FL_CLASS_IS_INHERITED;
+ mc_clear(mrb);
return;
}
for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
- if (mc[i].c == c || mc[i].mid == mid)
- mc[i].c = 0;
+ if (mc[i].c == c) mc[i].c = 0;
}
}
#endif
@@ -1321,10 +1746,9 @@ mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid)
MRB_API mrb_method_t
mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
{
- khiter_t k;
mrb_method_t m;
struct RClass *c = *cp;
-#ifdef MRB_METHOD_CACHE
+#ifndef MRB_NO_METHOD_CACHE
struct RClass *oc = c;
int h = kh_int_hash_func(mrb, ((intptr_t)oc) ^ mid) & (MRB_METHOD_CACHE_SIZE-1);
struct mrb_cache_entry *mc = &mrb->cache[h];
@@ -1336,15 +1760,23 @@ mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
#endif
while (c) {
- khash_t(mt) *h = c->mt;
+ mt_tbl *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;
+ struct mt_elem *e = mt_get(mrb, h, mid);
+ if (e) {
+ if (e->ptr.proc == 0) break;
*cp = c;
-#ifdef MRB_METHOD_CACHE
+ if (e->func_p) {
+ MRB_METHOD_FROM_FUNC(m, e->ptr.func);
+ }
+ else {
+ MRB_METHOD_FROM_PROC(m, e->ptr.proc);
+ }
+ if (e->noarg_p) {
+ MRB_METHOD_NOARG_SET(m);
+ }
+#ifndef MRB_NO_METHOD_CACHE
mc->c = oc;
mc->c0 = c;
mc->mid = mid;
@@ -1366,48 +1798,78 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
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);
+ mrb_name_error(mrb, mid, "undefined method '%n' for class %C", mid, c);
}
return m;
}
+#define ONSTACK_ALLOC_MAX 32
+
+static mrb_sym
+prepare_name_common(mrb_state *mrb, mrb_sym sym, const char *prefix, const char *suffix)
+{
+ char onstack[ONSTACK_ALLOC_MAX];
+ mrb_int sym_len;
+ const char *sym_str = mrb_sym_name_len(mrb, sym, &sym_len);
+ size_t prefix_len = prefix ? strlen(prefix) : 0;
+ size_t suffix_len = suffix ? strlen(suffix) : 0;
+ size_t name_len = sym_len + prefix_len + suffix_len;
+ char *buf = name_len > sizeof(onstack) ? (char *)mrb_alloca(mrb, name_len) : onstack;
+ char *p = buf;
+
+ if (prefix_len > 0) {
+ memcpy(p, prefix, prefix_len);
+ p += prefix_len;
+ }
+
+ memcpy(p, sym_str, sym_len);
+ p += sym_len;
+
+ if (suffix_len > 0) {
+ memcpy(p, suffix, suffix_len);
+ p += suffix_len;
+ }
+
+ return mrb_intern(mrb, buf, name_len);
+}
+
static mrb_value
-attr_reader(mrb_state *mrb, mrb_value obj)
+prepare_ivar_name(mrb_state *mrb, mrb_sym sym)
{
- mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
- return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+ sym = prepare_name_common(mrb, sym, "@", NULL);
+ mrb_iv_name_sym_check(mrb, sym);
+ return mrb_symbol_value(sym);
+}
+
+static mrb_sym
+prepare_writer_name(mrb_state *mrb, mrb_sym sym)
+{
+ return prepare_name_common(mrb, sym, NULL, "=");
}
static mrb_value
-mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state *, mrb_value), mrb_sym (*access_name)(mrb_state *, mrb_sym))
{
struct RClass *c = mrb_class_ptr(mod);
- mrb_value *argv;
+ const 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<argc; i++) {
- mrb_value name, str;
- mrb_sym method, sym;
+ mrb_value name;
+ mrb_sym method;
struct RProc *p;
mrb_method_t m;
method = to_sym(mrb, argv[i]);
- name = mrb_sym2str(mrb, method);
- str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_name_sym_check(mrb, sym);
- name = mrb_symbol_value(sym);
- p = mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name);
+ name = prepare_ivar_name(mrb, method);
+ if (access_name) {
+ method = access_name(mrb, method);
+ }
+
+ p = mrb_proc_new_cfunc_with_env(mrb, accessor, 1, &name);
MRB_METHOD_FROM_PROC(m, p);
mrb_define_method_raw(mrb, c, method, m);
mrb_gc_arena_restore(mrb, ai);
@@ -1416,12 +1878,24 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
}
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)
+{
+ return mod_attr_define(mrb, mod, attr_reader, NULL);
+}
+
+static mrb_value
attr_writer(mrb_state *mrb, mrb_value obj)
{
mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
- mrb_value val;
+ mrb_value val = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &val);
mrb_iv_set(mrb, obj, to_sym(mrb, name), val);
return val;
}
@@ -1429,42 +1903,7 @@ attr_writer(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_mod_attr_writer(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<argc; i++) {
- mrb_value name, str, attr;
- mrb_sym method, sym;
- struct RProc *p;
- mrb_method_t m;
-
- method = to_sym(mrb, argv[i]);
-
- /* prepare iv name (@name) */
- name = mrb_sym2str(mrb, method);
- str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_name_sym_check(mrb, sym);
- attr = mrb_symbol_value(sym);
-
- /* prepare method name (name=) */
- str = mrb_str_new_capa(mrb, RSTRING_LEN(str));
- mrb_str_cat_str(mrb, str, name);
- mrb_str_cat_lit(mrb, str, "=");
- method = mrb_intern_str(mrb, str);
-
- p = mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr);
- MRB_METHOD_FROM_PROC(m, p);
- mrb_define_method_raw(mrb, c, method, m);
- mrb_gc_arena_restore(mrb, ai);
- }
- return mrb_nil_value();
+ return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name);
}
static mrb_value
@@ -1479,7 +1918,7 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
if (ttype == 0) ttype = MRB_TT_OBJECT;
if (ttype <= MRB_TT_CPTR) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %S", cv);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %v", cv);
}
o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c);
return mrb_obj_value(o);
@@ -1497,29 +1936,20 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
*
*/
-MRB_API mrb_value
+mrb_value
mrb_instance_new(mrb_state *mrb, mrb_value cv)
{
mrb_value obj, blk;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc;
mrb_sym init;
- mrb_method_t m;
- mrb_get_args(mrb, "*&", &argv, &argc, &blk);
+ 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 {
+ init = MRB_SYM(initialize);
+ if (!mrb_func_basic_p(mrb, obj, init, mrb_bob_init)) {
mrb_funcall_with_block(mrb, obj, init, argc, argv, blk);
}
-
return obj;
}
@@ -1530,7 +1960,7 @@ mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *arg
mrb_sym mid;
obj = mrb_instance_alloc(mrb, mrb_obj_value(c));
- mid = mrb_intern_lit(mrb, "initialize");
+ mid = MRB_SYM(initialize);
if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) {
mrb_funcall_argv(mrb, obj, mid, argc, argv);
}
@@ -1562,8 +1992,11 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv)
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)) {
+ mid = MRB_SYM(initialize);
+ if (mrb_func_basic_p(mrb, new_class, mid, mrb_class_initialize)) {
+ mrb_class_initialize(mrb, new_class);
+ }
+ else {
mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk);
}
mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class));
@@ -1630,21 +2063,11 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv)
mrb_value
mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
+ mrb_value arg = mrb_get_arg1(mrb);
- 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)
{
@@ -1667,7 +2090,7 @@ MRB_API mrb_value
mrb_class_path(mrb_state *mrb, struct RClass *c)
{
mrb_value path;
- mrb_sym nsym = mrb_intern_lit(mrb, "__classname__");
+ mrb_sym nsym = MRB_SYM(__classname__);
path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym);
if (mrb_nil_p(path)) {
@@ -1676,11 +2099,7 @@ mrb_class_path(mrb_state *mrb, struct RClass *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_sym_str(mrb, mrb_symbol(path));
}
return mrb_str_dup(mrb, path);
}
@@ -1699,14 +2118,11 @@ mrb_class_real(struct RClass* 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, "#<Module:") :
- mrb_str_new_lit(mrb, "#<Class:");
- mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, c));
- mrb_str_cat_lit(mrb, path, ">");
- }
- return RSTRING_PTR(path);
+ mrb_value name;
+
+ if (c == NULL) return NULL;
+ name = class_name_str(mrb, c);
+ return RSTRING_PTR(name);
}
MRB_API const char*
@@ -1725,7 +2141,7 @@ 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));
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%C given)", super);
}
if (super->tt == MRB_TT_SCLASS) {
mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class");
@@ -1764,7 +2180,7 @@ mrb_class_new(mrb_state *mrb, struct RClass *super)
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);
+ struct RClass *m = MRB_OBJ_ALLOC(mrb, MRB_TT_MODULE, mrb->module_class);
boot_initmod(mrb, m);
return m;
}
@@ -1779,7 +2195,7 @@ mrb_module_new(mrb_state *mrb)
* called with an explicit receiver, as <code>class</code> is also a
* reserved word in Ruby.
*
- * 1.class #=> Fixnum
+ * 1.class #=> Integer
* self.class #=> Object
*/
@@ -1792,13 +2208,35 @@ mrb_obj_class(mrb_state *mrb, mrb_value obj)
MRB_API void
mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
{
+ if (a == b) return;
mrb_method_t m = mrb_method_search(mrb, c, b);
+ if (!MRB_METHOD_CFUNC_P(m)) {
+ struct RProc *p = MRB_METHOD_PROC(m);
+
+ if (MRB_PROC_ENV_P(p)) {
+ MRB_PROC_ENV(p)->mid = b;
+ }
+ else if (p->color != MRB_GC_RED) {
+ struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
+ struct REnv *e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, NULL);
+
+ e->mid = b;
+ if (tc) {
+ e->c = tc;
+ mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
+ }
+ p->e.env = e;
+ p->flags |= MRB_PROC_ENVSET;
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+ }
+ }
mrb_define_method_raw(mrb, c, a, m);
}
/*!
* Defines an alias of a method.
+ * \param mrb the mruby state
* \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
@@ -1809,6 +2247,12 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const
mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2));
}
+MRB_API void
+mrb_define_alias_id(mrb_state *mrb, struct RClass *klass, mrb_sym a, mrb_sym b)
+{
+ mrb_alias_method(mrb, klass, a, b);
+}
+
/*
* call-seq:
* mod.to_s -> string
@@ -1818,15 +2262,12 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const
* show information on the thing we're attached to as well.
*/
-static mrb_value
+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, "#<Class:");
+ if (mrb_sclass_p(klass)) {
+ mrb_value v = mrb_iv_get(mrb, klass, MRB_SYM(__attached__));
+ mrb_value str = mrb_str_new_lit(mrb, "#<Class:");
if (class_ptr_p(v)) {
mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
@@ -1837,37 +2278,12 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
return mrb_str_cat_lit(mrb, str, ">");
}
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, "#<Class:");
- break;
-
- case MRB_TT_MODULE:
- mrb_str_cat_lit(mrb, str, "#<Module:");
- break;
-
- default:
- /* Shouldn't be happened? */
- mrb_str_cat_lit(mrb, str, "#<??????:");
- break;
- }
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c));
- return mrb_str_cat_lit(mrb, str, ">");
- }
- else {
- return path;
- }
+ return class_name_str(mrb, mrb_class_ptr(klass));
}
}
+void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid);
+
static mrb_value
mrb_mod_alias(mrb_state *mrb, mrb_value mod)
{
@@ -1876,27 +2292,38 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "nn", &new_name, &old_name);
mrb_alias_method(mrb, c, new_name, old_name);
- return mrb_nil_value();
+ mrb_method_added(mrb, c, new_name);
+ return mod;
}
-void
+static void
+undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
+{
+ mrb_method_t m;
+
+ MRB_METHOD_FROM_PROC(m, NULL);
+ mrb_define_method_raw(mrb, c, a, m);
+}
+
+MRB_API 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_name_error(mrb, a, "undefined method '%n' for class '%C'", a, c);
}
+ undef_method(mrb, c, a);
}
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));
+ undef_method(mrb, c, mrb_intern_cstr(mrb, name));
+}
+
+MRB_API void
+mrb_undef_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name)
+{
+ mrb_undef_method_id(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name);
}
MRB_API void
@@ -1905,12 +2332,24 @@ 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);
}
+MRB_API void
+mrb_remove_method(mrb_state *mrb, struct RClass *c, mrb_sym mid)
+{
+ mt_tbl *h;
+
+ MRB_CLASS_ORIGIN(c);
+ h = c->mt;
+
+ if (h && mt_del(mrb, h, mid)) return;
+ mrb_name_error(mrb, mid, "method '%n' not defined in %C", mid, c);
+}
+
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;
+ const mrb_value *argv;
mrb_get_args(mrb, "*", &argv, &argc);
while (argc--) {
@@ -1921,17 +2360,13 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod)
}
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));
+ mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, id, &len);
+ if (!mrb_const_name_p(mrb, name, len)) {
+ mrb_name_error(mrb, id, "wrong constant name %n", id);
+ }
}
static mrb_value
@@ -1958,13 +2393,11 @@ mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id)
static mrb_value
mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
{
- mrb_value path;
+ mrb_value path = mrb_get_arg1(mrb);
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);
@@ -1972,7 +2405,7 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
}
/* const get with class path string */
- path = mrb_string_type(mrb, path);
+ path = mrb_ensure_string_type(mrb, path);
ptr = RSTRING_PTR(path);
len = RSTRING_LEN(path);
off = 0;
@@ -1987,7 +2420,7 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
else {
off = end + 2;
if (off == len) { /* trailing "::" */
- mrb_name_error(mrb, id, "wrong constant name '%S'", path);
+ mrb_name_error(mrb, id, "wrong constant name '%v'", path);
}
}
}
@@ -2017,7 +2450,7 @@ mrb_mod_remove_const(mrb_state *mrb, mrb_value mod)
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));
+ mrb_name_error(mrb, id, "constant %n not defined", id);
}
return val;
}
@@ -2030,13 +2463,10 @@ mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
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));
+ mrb_name_error(mrb, sym, "uninitialized constant %v::%n", mod, sym);
}
else {
- mrb_name_error(mrb, sym, "uninitialized constant %S",
- mrb_sym2str(mrb, sym));
+ mrb_name_error(mrb, sym, "uninitialized constant %n", sym);
}
/* not reached */
return mrb_nil_value();
@@ -2078,10 +2508,25 @@ mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
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)
+void
+mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid)
+{
+ mrb_sym added;
+ mrb_value recv = mrb_obj_value(c);
+
+ if (c->tt == MRB_TT_SCLASS) {
+ added = MRB_SYM(singleton_method_added);
+ recv = mrb_iv_get(mrb, recv, MRB_SYM(__attached__));
+ }
+ else {
+ added = MRB_SYM(method_added);
+ }
+ mrb_funcall_id(mrb, recv, added, 1, mrb_symbol_value(mid));
+}
+
+mrb_value
+mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c)
{
- struct RClass *c = mrb_class_ptr(self);
struct RProc *p;
mrb_method_t m;
mrb_sym mid;
@@ -2097,42 +2542,56 @@ mod_define_method(mrb_state *mrb, mrb_value self)
/* ignored */
break;
default:
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc)));
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %T (expected Proc)", 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);
+ p = 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);
+ mrb_method_added(mrb, c, mid);
return mrb_symbol_value(mid);
}
static mrb_value
+mod_define_method(mrb_state *mrb, mrb_value self)
+{
+ return mrb_mod_define_method_m(mrb, mrb_class_ptr(self));
+}
+
+static mrb_value
top_define_method(mrb_state *mrb, mrb_value self)
{
- return mod_define_method(mrb, mrb_obj_value(mrb->object_class));
+ return mrb_mod_define_method_m(mrb, mrb->object_class);
}
static mrb_value
mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
{
- mrb_value obj;
+ mrb_value obj = mrb_get_arg1(mrb);
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
+static mrb_value
+mrb_mod_dup(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mod = mrb_obj_clone(mrb, self);
+ MRB_UNSET_FROZEN_FLAG(mrb_obj_ptr(mod));
+ return mod;
+}
+
+static mrb_value
mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
{
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc, i;
mrb_sym mid;
mrb_method_t m;
@@ -2166,6 +2625,202 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
return mod;
}
+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_SYM(__attached__), obj);
+ }
+ if (klass->mt) {
+ clone->mt = mt_copy(mrb, klass->mt);
+ }
+ else {
+ clone->mt = mt_new(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 = mt_copy(mrb, sc->mt);
+ }
+ else {
+ dc->mt = mt_new(mrb);
+ }
+ dc->super = sc->super;
+ MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc));
+}
+
+/* 15.3.1.3.16 */
+static mrb_value
+mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
+{
+ mrb_value orig = mrb_get_arg1(mrb);
+
+ 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;
+}
+
+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_SYM(__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;
+ }
+ if (!mrb_func_basic_p(mrb, dest, MRB_SYM(initialize_copy), mrb_obj_init_copy)) {
+ mrb_funcall_id(mrb, dest, MRB_SYM(initialize_copy), 1, obj);
+ }
+}
+
+/* 15.3.1.3.8 */
+/*
+ * call-seq:
+ * obj.clone -> an_object
+ *
+ * Produces a shallow copy of <i>obj</i>---the instance variables of
+ * <i>obj</i> are copied, but not the objects they reference. Copies
+ * the frozen state of <i>obj</i>. See also the discussion
+ * under <code>Object#dup</code>.
+ *
+ * class Klass
+ * attr_accessor :str
+ * end
+ * s1 = Klass.new #=> #<Klass:0x401b3a38>
+ * s1.str = "Hello" #=> "Hello"
+ * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
+ * s2.str[1,4] = "i" #=> "i"
+ * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
+ * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
+ *
+ * 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 Integer 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)) {
+ return self;
+ }
+ if (mrb_sclass_p(self)) {
+ 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 <i>obj</i>---the instance variables of
+ * <i>obj</i> are copied, but not the objects they reference.
+ * <code>dup</code> copies the frozen state of <i>obj</i>. See also
+ * the discussion under <code>Object#clone</code>. In general,
+ * <code>clone</code> and <code>dup</code> may have different semantics
+ * in descendant classes. While <code>clone</code> is used to duplicate
+ * an object, including its internal state, <code>dup</code> 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)) {
+ return obj;
+ }
+ if (mrb_sclass_p(obj)) {
+ 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;
+}
+
/* implementation of __id__ */
mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
/* implementation of instance_eval */
@@ -2177,6 +2832,43 @@ inspect_main(mrb_state *mrb, mrb_value mod)
return mrb_str_new_lit(mrb, "main");
}
+static const mrb_code new_iseq[] = {
+ OP_ENTER, 0x0, 0x10, 0x1, /* OP_ENTER 0:0:1:0:0:0:1 */
+ OP_LOADSELF, 0x3, /* OP_LOADSELF R3 */
+ OP_SEND, 0x3, 0x0, 0x0, /* OP_SEND R3 :allocate 0 */
+ OP_MOVE, 0x0, 0x3, /* OP_MOVE R0 R3 */
+ OP_MOVE, 0x4, 0x1, /* OP_MOVE R4 R1 */
+ OP_MOVE, 0x5, 0x2, /* OP_MOVE R5 R2 */
+ OP_SENDVB, 0x3, 0x1, /* OP_SENDVB R3 :initialize */
+ OP_RETURN, 0x0 /* OP_RETURN R0 */
+};
+
+MRB_PRESYM_DEFINE_VAR_AND_INITER(new_syms, 2, MRB_SYM(allocate), MRB_SYM(initialize))
+
+static const mrb_irep new_irep = {
+ 3, 6, 0, MRB_IREP_STATIC,
+ new_iseq, NULL, new_syms, NULL, NULL, NULL,
+ sizeof(new_iseq), 0, 2, 0, 0,
+};
+
+static const struct RProc new_proc = {
+ NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT,
+ { &new_irep }, NULL, { NULL }
+};
+
+static void
+init_class_new(mrb_state *mrb, struct RClass *cls)
+{
+ mrb_method_t m;
+
+ MRB_PRESYM_INIT_SYMBOLS(mrb, new_syms);
+ MRB_METHOD_FROM_PROC(m, &new_proc);
+ mrb_define_method_raw(mrb, cls, MRB_SYM(new), m);
+}
+
+/* implementation of #send method */
+mrb_value mrb_f_send(mrb_state *mrb, mrb_value self);
+
void
mrb_init_class(mrb_state *mrb)
{
@@ -2198,17 +2890,16 @@ mrb_init_class(mrb_state *mrb)
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));
+ mrb_define_const_id(mrb, bob, MRB_SYM(BasicObject), mrb_obj_value(bob));
+ mrb_define_const_id(mrb, obj, MRB_SYM(Object), mrb_obj_value(obj));
+ mrb_define_const_id(mrb, obj, MRB_SYM(Module), mrb_obj_value(mod));
+ mrb_define_const_id(mrb, obj, MRB_SYM(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_class_name_class(mrb, NULL, bob, MRB_SYM(BasicObject));
+ mrb_class_name_class(mrb, NULL, obj, MRB_SYM(Object)); /* 15.2.1 */
+ mrb_class_name_class(mrb, NULL, mod, MRB_SYM(Module)); /* 15.2.2 */
+ mrb_class_name_class(mrb, NULL, cls, MRB_SYM(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);
@@ -2217,17 +2908,20 @@ mrb_init_class(mrb_state *mrb)
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_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.5 */
+ mrb_define_method(mrb, bob, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
+ mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.3.1.3.18 */
+ mrb_define_method(mrb, bob, "singleton_method_added", mrb_bob_init, MRB_ARGS_REQ(1));
- mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, cls, "allocate", mrb_instance_alloc, MRB_ARGS_NONE());
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));
+ init_class_new(mrb, cls);
+
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 */
@@ -2257,12 +2951,16 @@ mrb_init_class(mrb_state *mrb)
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_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */
+ mrb_define_method(mrb, mod, "dup", mrb_mod_dup, MRB_ARGS_NONE());
+ mrb_define_method(mrb, bob, "method_added", mrb_bob_init, MRB_ARGS_REQ(1));
mrb_undef_method(mrb, cls, "append_features");
+ mrb_undef_method(mrb, cls, "prepend_features");
mrb_undef_method(mrb, cls, "extend_object");
+ mrb_undef_method(mrb, cls, "module_function");
- mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class);
+ mrb->top_self = 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));
diff --git a/src/codedump.c b/src/codedump.c
index 30a14f937..4d1d96171 100644
--- a/src/codedump.c
+++ b/src/codedump.c
@@ -4,25 +4,20 @@
#include <mruby/opcode.h>
#include <mruby/string.h>
#include <mruby/proc.h>
+#include <mruby/dump.h>
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
static void
-print_r(mrb_state *mrb, mrb_irep *irep, size_t n)
+print_r(mrb_state *mrb, const mrb_irep *irep, size_t n)
{
- size_t i;
-
if (n == 0) return;
-
- for (i=0; i+1<irep->nlocals; i++) {
- if (irep->lv[i].r == n) {
- mrb_sym sym = irep->lv[i].name;
- printf(" R%d:%s", (int)n, mrb_sym2name(mrb, sym));
- }
- }
+ if (n >= irep->nlocals) return;
+ if (!irep->lv[n-1]) return;
+ printf(" R%d:%s", (int)n, mrb_sym_dump(mrb, irep->lv[n-1]));
}
static void
-print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a)
+print_lv_a(mrb_state *mrb, const mrb_irep *irep, uint16_t a)
{
if (!irep->lv || a >= irep->nlocals || a == 0) {
printf("\n");
@@ -34,7 +29,7 @@ print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a)
}
static void
-print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b)
+print_lv_ab(mrb_state *mrb, const mrb_irep *irep, uint16_t a, uint16_t b)
{
if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) {
printf("\n");
@@ -47,11 +42,11 @@ print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b)
}
static void
-print_header(mrb_irep *irep, ptrdiff_t i)
+print_header(mrb_state *mrb, const mrb_irep *irep, uint32_t i)
{
int32_t line;
- line = mrb_debug_get_line(irep, i);
+ line = mrb_debug_get_line(mrb, irep, i);
if (line < 0) {
printf(" ");
}
@@ -65,24 +60,52 @@ print_header(mrb_irep *irep, ptrdiff_t i)
#define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn
static void
-codedump(mrb_state *mrb, mrb_irep *irep)
+codedump(mrb_state *mrb, const mrb_irep *irep)
{
int ai;
- mrb_code *pc, *pcend;
+ const 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);
+ printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d iseq=%d\n", (void*)irep,
+ irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen);
if (irep->lv) {
int i;
printf("local variable names:\n");
for (i = 1; i < irep->nlocals; ++i) {
- char const *n = mrb_sym2name(mrb, irep->lv[i - 1].name);
- printf(" R%d:%s\n", irep->lv[i - 1].r, n? n : "");
+ char const *s = mrb_sym_dump(mrb, irep->lv[i - 1]);
+ printf(" R%d:%s\n", i, s ? s : "");
+ }
+ }
+
+ if (irep->clen > 0) {
+ int i = irep->clen;
+ const struct mrb_irep_catch_handler *e = mrb_irep_catch_handler_table(irep);
+
+ for (; i > 0; i --, e ++) {
+ uint32_t begin = mrb_irep_catch_handler_unpack(e->begin);
+ uint32_t end = mrb_irep_catch_handler_unpack(e->end);
+ uint32_t target = mrb_irep_catch_handler_unpack(e->target);
+ char buf[20];
+ const char *type;
+
+ switch (e->type) {
+ case MRB_CATCH_RESCUE:
+ type = "rescue";
+ break;
+ case MRB_CATCH_ENSURE:
+ type = "ensure";
+ break;
+ default:
+ buf[0] = '\0';
+ snprintf(buf, sizeof(buf), "0x%02x <unknown>", (int)e->type);
+ type = buf;
+ break;
+ }
+ printf("catch type: %-8s begin: %04" PRIu32 " end: %04" PRIu32 " target: %04" PRIu32 "\n", type, begin, end, target);
}
}
@@ -92,17 +115,17 @@ codedump(mrb_state *mrb, mrb_irep *irep)
ptrdiff_t i;
uint32_t a;
uint16_t b;
- uint8_t c;
+ uint16_t c;
ai = mrb_gc_arena_save(mrb);
i = pc - irep->iseq;
- next_file = mrb_debug_get_filename(irep, i);
+ next_file = mrb_debug_get_filename(mrb, irep, (uint32_t)i);
if (next_file && file != next_file) {
printf("file: %s\n", next_file);
file = next_file;
}
- print_header(irep, i);
+ print_header(mrb, irep, (uint32_t)i);
ins = READ_B();
switch (ins) {
CASE(OP_NOP, Z):
@@ -112,11 +135,25 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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));
+ switch (irep->pool[b].tt) {
+#ifndef MRB_NO_FLOAT
+ case IREP_TT_FLOAT:
+ printf("OP_LOADL\tR%d\tL(%d)\t; %f", a, b, (double)irep->pool[b].u.f);
+ break;
+#endif
+ case IREP_TT_INT32:
+ printf("OP_LOADL\tR%d\tL(%d)\t; %" PRId32, a, b, irep->pool[b].u.i32);
+ break;
+#ifdef MRB_64BIT
+ case IREP_TT_INT64:
+ printf("OP_LOADL\tR%d\tL(%d)\t; %" PRId64, a, b, irep->pool[b].u.i64);
+ break;
+#endif
+ default:
+ printf("OP_LOADL\tR%d\tL(%d)\t", a, b);
+ break;
}
print_lv_a(mrb, irep, a);
break;
@@ -128,6 +165,14 @@ codedump(mrb_state *mrb, mrb_irep *irep)
printf("OP_LOADI\tR%d\t-%d\t", a, b);
print_lv_a(mrb, irep, a);
break;
+ CASE(OP_LOADI16, BS):
+ printf("OP_LOADI16\tR%d\t%d\t", a, (int)(int16_t)b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI32, BSS):
+ printf("OP_LOADI32\tR%d\t%d\t", a, (int32_t)(((uint32_t)b<<16)+c));
+ 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);
@@ -145,7 +190,7 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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]));
+ printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
print_lv_a(mrb, irep, a);
break;
CASE(OP_LOADNIL, B):
@@ -165,43 +210,43 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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]));
+ printf("OP_GETGV\tR%d\t:%s", a, mrb_sym_dump(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);
+ printf("OP_SETGV\t:%s\tR%d", mrb_sym_dump(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]));
+ printf("OP_GETSV\tR%d\t:%s", a, mrb_sym_dump(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);
+ printf("OP_SETSV\t:%s\tR%d", mrb_sym_dump(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]));
+ printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym_dump(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);
+ printf("OP_SETCONST\t:%s\tR%d", mrb_sym_dump(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]));
+ printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym_dump(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);
+ printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym_dump(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]));
+ printf("OP_GETIV\tR%d\t%s", a, mrb_sym_dump(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);
+ printf("OP_SETIV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
print_lv_a(mrb, irep, a);
break;
CASE(OP_GETUPVAR, BBB):
@@ -213,39 +258,50 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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]));
+ printf("OP_GETCV\tR%d\t%s", a, mrb_sym_dump(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);
+ printf("OP_SETCV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
print_lv_a(mrb, irep, a);
break;
CASE(OP_JMP, S):
- printf("OP_JMP\t\t%03d\n", a);
+ i = pc - irep->iseq;
+ printf("OP_JMP\t\t%03d\n", (int)i+(int16_t)a);
+ break;
+ CASE(OP_JMPUW, S):
+ i = pc - irep->iseq;
+ printf("OP_JMPUW\t\t%03d\n", (int)i+(int16_t)a);
break;
CASE(OP_JMPIF, BS):
- printf("OP_JMPIF\tR%d\t%03d\t", a, b);
+ i = pc - irep->iseq;
+ printf("OP_JMPIF\tR%d\t%03d\t", a, (int)i+(int16_t)b);
print_lv_a(mrb, irep, a);
break;
CASE(OP_JMPNOT, BS):
- printf("OP_JMPNOT\tR%d\t%03d\t", a, b);
+ i = pc - irep->iseq;
+ printf("OP_JMPNOT\tR%d\t%03d\t", a, (int)i+(int16_t)b);
print_lv_a(mrb, irep, a);
break;
CASE(OP_JMPNIL, BS):
- printf("OP_JMPNIL\tR%d\t%03d\t", a, b);
+ i = pc - irep->iseq;
+ printf("OP_JMPNIL\tR%d\t%03d\t", a, (int)i+(int16_t)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]));
+ printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
break;
CASE(OP_SENDVB, BB):
- printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym_dump(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);
+ printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym_dump(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);
+ printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym_dump(mrb, irep->syms[b]), c);
+ break;
+ CASE(OP_SENDVK, BB):
+ printf("OP_SENDVK\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
break;
CASE(OP_CALL, Z):
printf("OP_CALL\n");
@@ -264,23 +320,23 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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);
+ MRB_ASPEC_REQ(a),
+ MRB_ASPEC_OPT(a),
+ MRB_ASPEC_REST(a),
+ MRB_ASPEC_POST(a),
+ MRB_ASPEC_KEY(a),
+ MRB_ASPEC_KDICT(a),
+ MRB_ASPEC_BLOCK(a));
break;
CASE(OP_KEY_P, BB):
- printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
+ printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym_dump(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]));
+ printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
print_lv_a(mrb, irep, a);
break;
CASE(OP_RETURN, B):
@@ -305,13 +361,13 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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]);
+ printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]);
break;
CASE(OP_BLOCK, BB):
- printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+ printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]);
break;
CASE(OP_METHOD, BB):
- printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+ printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]);
break;
CASE(OP_RANGE_INC, B):
printf("OP_RANGE_INC\tR%d\n", a);
@@ -320,46 +376,46 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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]));
+ printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
break;
CASE(OP_UNDEF, B):
- printf("OP_UNDEF\t:%s\n", mrb_sym2name(mrb, irep->syms[a]));
+ printf("OP_UNDEF\t:%s\n", mrb_sym_dump(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]));
+ printf("OP_ALIAS\t:%s\t%s\n", mrb_sym_dump(mrb, irep->syms[a]), mrb_sym_dump(mrb, irep->syms[b]));
break;
- CASE(OP_ADD, BB):
- printf("OP_ADD\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_ADD, B):
+ printf("OP_ADD\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_ADDI, BBB):
- printf("OP_ADDI\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c);
+ CASE(OP_ADDI, BB):
+ printf("OP_ADDI\tR%d\t%d\n", a, b);
break;
- CASE(OP_SUB, BB):
- printf("OP_SUB\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_SUB, B):
+ printf("OP_SUB\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_SUBI, BBB):
- printf("OP_SUBI\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c);
+ CASE(OP_SUBI, BB):
+ printf("OP_SUBI\tR%d\t%d\n", a, b);
break;
- CASE(OP_MUL, BB):
- printf("OP_MUL\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_MUL, B):
+ printf("OP_MUL\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_DIV, BB):
- printf("OP_DIV\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_DIV, B):
+ printf("OP_DIV\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_LT, BB):
- printf("OP_LT\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_LT, B):
+ printf("OP_LT\t\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_LE, BB):
- printf("OP_LE\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_LE, B):
+ printf("OP_LE\t\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_GT, BB):
- printf("OP_GT\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_GT, B):
+ printf("OP_GT\t\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_GE, BB):
- printf("OP_GE\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_GE, B):
+ printf("OP_GE\t\tR%d\tR%d\n", a, a+1);
break;
- CASE(OP_EQ, BB):
- printf("OP_EQ\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ CASE(OP_EQ, B):
+ printf("OP_EQ\t\tR%d\tR%d\n", a, a+1);
break;
CASE(OP_ARRAY, BB):
printf("OP_ARRAY\tR%d\t%d\t", a, b);
@@ -398,10 +454,11 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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));
+ if ((irep->pool[b].tt & IREP_TT_NFLAG) == 0) {
+ printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, irep->pool[b].u.str);
+ }
+ else {
+ printf("OP_STRING\tR%d\tL(%d)\t", a, b);
}
print_lv_a(mrb, irep, a);
break;
@@ -427,15 +484,15 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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]));
+ printf("OP_CLASS\tR%d\t:%s", a, mrb_sym_dump(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]));
+ printf("OP_MODULE\tR%d\t:%s", a, mrb_sym_dump(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]);
+ printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, (void*)irep->reps[b]);
print_lv_a(mrb, irep, a);
break;
CASE(OP_SCLASS, B):
@@ -447,17 +504,12 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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));
+ if ((irep->pool[a].tt & IREP_TT_NFLAG) == 0) {
+ printf("OP_ERR\t%s\n", irep->pool[a].u.str);
+ }
+ else {
+ printf("OP_ERR\tL(%d)\n", a);
}
- 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);
@@ -467,16 +519,10 @@ codedump(mrb_state *mrb, mrb_irep *irep)
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);
+ CASE(OP_RAISEIF, B):
+ printf("OP_RAISEIF\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);
@@ -489,7 +535,7 @@ codedump(mrb_state *mrb, mrb_irep *irep)
CASE(OP_EXT1, Z):
ins = READ_B();
printf("OP_EXT1\n");
- print_header(irep, pc-irep->iseq-2);
+ print_header(mrb, 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"
@@ -499,7 +545,7 @@ codedump(mrb_state *mrb, mrb_irep *irep)
CASE(OP_EXT2, Z):
ins = READ_B();
printf("OP_EXT2\n");
- print_header(irep, pc-irep->iseq-2);
+ print_header(mrb, 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"
@@ -509,7 +555,7 @@ codedump(mrb_state *mrb, mrb_irep *irep)
CASE(OP_EXT3, Z):
ins = READ_B();
printf("OP_EXT3\n");
- print_header(irep, pc-irep->iseq-2);
+ print_header(mrb, 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"
@@ -527,13 +573,15 @@ codedump(mrb_state *mrb, mrb_irep *irep)
}
static void
-codedump_recur(mrb_state *mrb, mrb_irep *irep)
+codedump_recur(mrb_state *mrb, const mrb_irep *irep)
{
int i;
codedump(mrb, irep);
- for (i=0; i<irep->rlen; i++) {
- codedump_recur(mrb, irep->reps[i]);
+ if (irep->reps) {
+ for (i=0; i<irep->rlen; i++) {
+ codedump_recur(mrb, irep->reps[i]);
+ }
}
}
#endif
@@ -541,7 +589,7 @@ codedump_recur(mrb_state *mrb, mrb_irep *irep)
void
mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
{
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
codedump_recur(mrb, proc->body.irep);
#endif
}
diff --git a/src/crc.c b/src/crc.c
deleted file mode 100644
index 290b2ca0e..000000000
--- a/src/crc.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-** crc.c - calculate CRC
-**
-** See Copyright Notice in mruby.h
-*/
-
-#include <limits.h>
-#include <stdint.h>
-#include <stddef.h>
-
-/* 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);
-}
-
diff --git a/src/debug.c b/src/debug.c
index e55f11d4f..203dcf981 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -35,43 +35,96 @@ get_file(mrb_irep_debug_info *info, uint32_t pc)
return *ret;
}
+static size_t
+packed_int_len(uint32_t num)
+{
+ size_t llen = 0;
+
+ do {
+ llen++;
+ } while (num >>= 7);
+ return llen;
+}
+
+static size_t
+packed_int_encode(uint32_t num, uint8_t *p, uint8_t *pend)
+{
+ size_t llen = 0;
+
+ do {
+ uint8_t byte = num & 0x7f;
+ num >>= 7;
+ if (num != 0) byte |= 0x80;
+ if (p < pend) *p++ = byte;
+ llen++;
+ } while (num != 0);
+
+ return llen;
+}
+
+static uint32_t
+packed_int_decode(uint8_t *p, uint8_t **newpos)
+{
+ size_t i = 0, shift = 0;
+ uint32_t n = 0;
+
+ do {
+ n |= ((uint32_t)(p[i] & 0x7f)) << shift;
+ i++;
+ shift += 7;
+ } while (shift < sizeof(uint32_t) * 8 && (p[i - 1] & 0x80));
+ if (newpos) *newpos = p + i;
+ return n;
+}
+
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 packed_map_len = 0;
+ uint32_t prev_line = 0;
+ uint32_t prev_pc = 0;
size_t i;
for (i = 0; i < lines_len; ++i) {
if (lines[i] != prev_line) {
+ packed_map_len += packed_int_len(lines[i]-prev_line);
+ prev_line = lines[i];
+ packed_map_len += packed_int_len(i-prev_pc);
+ prev_pc = i;
++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;
+ size_t line_ary_len = sizeof(uint16_t) * lines_len;
+ size_t flat_map_len = sizeof(mrb_irep_debug_info_line) * line_count;
+ if (line_ary_len < flat_map_len && line_ary_len < packed_map_len) {
+ return mrb_debug_line_ary;
+ }
+ if (flat_map_len < packed_map_len) return mrb_debug_line_flat_map;
+ return mrb_debug_line_packed_map;
}
MRB_API char const*
-mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc)
+mrb_debug_get_filename(mrb_state *mrb, const mrb_irep *irep, uint32_t pc)
{
- if (irep && pc >= 0 && pc < irep->ilen) {
+ if (irep && pc < irep->ilen) {
mrb_irep_debug_info_file* f = NULL;
- if (!irep->debug_info) { return irep->filename; }
- else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
- return f->filename;
+ if (!irep->debug_info) return NULL;
+ else if ((f = get_file(irep->debug_info, pc))) {
+ return mrb_sym_name_len(mrb, f->filename_sym, NULL);
}
}
return NULL;
}
MRB_API int32_t
-mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc)
+mrb_debug_get_line(mrb_state *mrb, const mrb_irep *irep, uint32_t pc)
{
- if (irep && pc >= 0 && pc < irep->ilen) {
+ if (irep && pc < irep->ilen) {
mrb_irep_debug_info_file* f = NULL;
if (!irep->debug_info) {
- return irep->lines? irep->lines[pc] : -1;
+ return -1;
}
- else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
+ else if ((f = get_file(irep->debug_info, pc))) {
switch (f->line_type) {
case mrb_debug_line_ary:
mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count));
@@ -102,6 +155,19 @@ mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc)
return ret->line;
}
+
+ case mrb_debug_line_packed_map: {
+ uint8_t *p = f->lines.packed_map;
+ uint8_t *pend = p + f->line_entry_count;
+ uint32_t pos = 0, line = 0, line_diff;
+ while (p < pend) {
+ pos += packed_int_decode(p, &p);
+ line_diff = packed_int_decode(p, &p);
+ if (pc < pos) break;
+ line += line_diff;
+ }
+ return line;
+ }
}
}
}
@@ -122,82 +188,111 @@ 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 *irep,
+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 *info;
- mrb_irep_debug_info_file *ret;
+ mrb_irep_debug_info_file *f;
uint32_t file_pc_count;
size_t fn_len;
- mrb_int len;
uint32_t i;
- if (!irep->debug_info) { return NULL; }
-
- mrb_assert(irep->filename);
- mrb_assert(irep->lines);
+ if (!d) return NULL;
+ if (start_pos == end_pos) return NULL;
- info = irep->debug_info;
+ mrb_assert(filename);
+ mrb_assert(lines);
- if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 0) {
- return NULL;
+ if (d->flen > 0) {
+ const char *fn = mrb_sym_name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL);
+ if (strcmp(filename, fn) == 0)
+ return NULL;
}
- ret = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*ret));
- info->files =
- (mrb_irep_debug_info_file**)(
- info->files
- ? mrb_realloc(mrb, info->files, sizeof(mrb_irep_debug_info_file*) * (info->flen + 1))
+ 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*)));
- info->files[info->flen++] = ret;
+ d->files[d->flen++] = f;
file_pc_count = end_pos - start_pos;
- ret->start_pos = start_pos;
- info->pc_count = end_pos;
+ f->start_pos = start_pos;
+ d->pc_count = end_pos;
- fn_len = strlen(irep->filename);
- ret->filename_sym = mrb_intern(mrb, irep->filename, fn_len);
- len = 0;
- ret->filename = mrb_sym2name_len(mrb, ret->filename_sym, &len);
+ fn_len = strlen(filename);
+ f->filename_sym = mrb_intern(mrb, filename, fn_len);
- ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos);
- ret->lines.ptr = NULL;
+ f->line_type = select_line_type(lines + start_pos, end_pos - start_pos);
+ f->lines.ptr = NULL;
- switch (ret->line_type) {
+ switch (f->line_type) {
case mrb_debug_line_ary:
- ret->line_entry_count = file_pc_count;
- ret->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
+ 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) {
- ret->lines.ary[i] = irep->lines[start_pos + 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;
- ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
- ret->line_entry_count = 0;
+ f->line_entry_count = 0;
for (i = 0; i < file_pc_count; ++i) {
- if (irep->lines[start_pos + i] == prev_line) { continue; }
+ if (lines[start_pos + i] == prev_line) { continue; }
+ ++f->line_entry_count;
+ prev_line = lines[start_pos + i];
+ }
+ f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * f->line_entry_count);
+ prev_line = 0;
+ for (i = 0; i < file_pc_count; ++i) {
+ if (lines[start_pos + i] == prev_line) { continue; }
- ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
- mrb, ret->lines.flat_map,
- sizeof(mrb_irep_debug_info_line) * (ret->line_entry_count + 1));
m.start_pos = start_pos + i;
- m.line = irep->lines[start_pos + i];
- ret->lines.flat_map[ret->line_entry_count] = m;
+ m.line = lines[start_pos + i];
+ f->lines.flat_map[f->line_entry_count] = m;
/* update */
- ++ret->line_entry_count;
- prev_line = irep->lines[start_pos + i];
+ prev_line = lines[start_pos + i];
}
- } break;
+ break;
+ }
+
+ case mrb_debug_line_packed_map: {
+ uint32_t prev_line = 0;
+ uint32_t prev_pc = 0;
+ size_t packed_size = 0;
+ uint8_t *p, *pend;
+
+ for (i = 0; i < file_pc_count; ++i) {
+ if (lines[start_pos + i] == prev_line) { continue; }
+
+ packed_size += packed_int_len(start_pos+i-prev_pc);
+ prev_pc = start_pos+i;
+ packed_size += packed_int_len(lines[start_pos+i]-prev_line);
+ prev_line = lines[start_pos + i];
+ }
+ p = f->lines.packed_map = (uint8_t*)mrb_malloc(mrb, packed_size);
+ pend = p + packed_size;
+ prev_line = 0; prev_pc = 0;
+ for (i = 0; i < file_pc_count; ++i) {
+ if (lines[start_pos + i] == prev_line) { continue; }
+ p += packed_int_encode(start_pos+i-prev_pc, p, pend);
+ prev_pc = start_pos + i;
+ p += packed_int_encode(lines[start_pos + i]-prev_line, p, pend);
+ prev_line = lines[start_pos + i];
+ /* update */
+ }
+ f->line_entry_count = (uint32_t)packed_size;
+ break;
+ }
default: mrb_assert(0); break;
}
- return ret;
+ return f;
}
MRB_API void
@@ -207,11 +302,14 @@ mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d)
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]);
+ if (d->files) {
+ for (i = 0; i < d->flen; ++i) {
+ if (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->files);
mrb_free(mrb, d);
}
diff --git a/src/dump.c b/src/dump.c
index df1e171e4..628c3dbc4 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -1,46 +1,28 @@
/*
-** dump.c - mruby binary dumper (mrbc binary format)
+** cdump.c - mruby binary dumper (in C)
**
** See Copyright Notice in mruby.h
*/
#include <string.h>
#include <limits.h>
+#include <math.h>
#include <mruby/dump.h>
#include <mruby/string.h>
#include <mruby/irep.h>
-#include <mruby/numeric.h>
#include <mruby/debug.h>
-#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
+#ifndef MRB_NO_FLOAT
+#include <mruby/endian.h>
#endif
-static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep);
+static size_t get_irep_record_size_1(mrb_state *mrb, const 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;
@@ -52,7 +34,7 @@ get_irep_header_size(mrb_state *mrb)
}
static ptrdiff_t
-write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
+write_irep_header(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf)
{
uint8_t *cur = buf;
@@ -64,95 +46,112 @@ write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
return cur - buf;
}
-
static size_t
-get_iseq_block_size(mrb_state *mrb, mrb_irep *irep)
+get_iseq_block_size(mrb_state *mrb, const 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) */
+ size += sizeof(uint16_t); /* clen */
+ size += sizeof(uint16_t); /* ilen */
+ size += irep->ilen * sizeof(mrb_code); /* iseq(n) */
+ size += irep->clen * sizeof(struct mrb_irep_catch_handler);
return size;
}
static ptrdiff_t
-write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
+write_iseq_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf, uint8_t flags)
{
uint8_t *cur = buf;
- int iseq_no;
-
- cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */
- cur += write_padding(cur);
- switch (flags & DUMP_ENDIAN_NAT) {
- case DUMP_ENDIAN_BIG:
- if (bigendian_p()) goto native;
- for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
- cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */
- }
- break;
- case DUMP_ENDIAN_LIL:
- if (!bigendian_p()) goto native;
- for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
- cur += uint32l_to_bin(irep->iseq[iseq_no], cur); /* opcode */
- }
- break;
+ size_t seqlen = irep->ilen * sizeof(mrb_code) +
+ irep->clen * sizeof(struct mrb_irep_catch_handler);
- native:
- case DUMP_ENDIAN_NAT:
- memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code));
- cur += irep->ilen * sizeof(mrb_code);
- break;
- }
+ cur += uint16_to_bin(irep->clen, cur); /* number of catch handlers */
+ cur += uint16_to_bin(irep->ilen, cur); /* number of opcode */
+ memcpy(cur, irep->iseq, seqlen);
+ cur += seqlen;
return cur - buf;
}
+#ifndef MRB_NO_FLOAT
+static void
+dump_float(mrb_state *mrb, uint8_t *buf, mrb_float f)
+{
+ /* dump IEEE754 binary in little endian */
+ union {
+ double f;
+ char s[sizeof(double)];
+ } u = {.f = (double)f};
+
+ if (littleendian) {
+ memcpy(buf, u.s, sizeof(double));
+ }
+ else {
+ size_t i;
+
+ for (i=0; i<sizeof(double); i++) {
+ buf[i] = u.s[sizeof(double)-i-1];
+ }
+ }
+}
+#endif
static size_t
-get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
+get_pool_block_size(mrb_state *mrb, const 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) */
+ size += sizeof(uint16_t); /* plen */
+ size += irep->plen * sizeof(uint8_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);
+ switch (irep->pool[pool_no].tt) {
+ case IREP_TT_INT64:
+#ifdef MRB_64BIT
{
- mrb_int len = RSTRING_LEN(str);
- mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
- size += (size_t)len;
+ int64_t i = irep->pool[pool_no].u.i64;
+
+ if (i < INT32_MIN || INT32_MAX < i)
+ size += 8;
+ else
+ size += 4;
}
break;
+#else
+ /* fall through */
+#endif
+ case IREP_TT_INT32:
+ size += 4; /* 32bits = 4bytes */
+ break;
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
- str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
+ case IREP_TT_BIGINT:
{
- mrb_int len = RSTRING_LEN(str);
+ mrb_int len = irep->pool[pool_no].u.str[0];
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
- size += (size_t)len;
+ size += sizeof(uint8_t);
+ size += (size_t)len+2;
}
break;
-#endif
- case MRB_TT_STRING:
+ case IREP_TT_FLOAT:
+#ifndef MRB_NO_FLOAT
{
- mrb_int len = RSTRING_LEN(irep->pool[pool_no]);
- mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
- size += (size_t)len;
+ size += sizeof(double);
}
+#endif
break;
- default:
+ default: /* packed IREP_TT_STRING */
+ {
+ mrb_int len = irep->pool[pool_no].tt >> 2; /* unpack length */
+ mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
+ size += sizeof(uint16_t);
+ size += (size_t)len+1;
+ }
break;
}
mrb_gc_arena_restore(mrb, ai);
@@ -162,71 +161,89 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
}
static ptrdiff_t
-write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
+write_pool_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf)
{
int pool_no;
uint8_t *cur = buf;
- uint16_t len;
- mrb_value str;
- const char *char_ptr;
+ mrb_int len;
+ const char *ptr;
- cur += uint32_to_bin(irep->plen, cur); /* number of pool */
+ cur += uint16_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);
+ switch (irep->pool[pool_no].tt) {
+#ifdef MRB_64BIT
+ case IREP_TT_INT64:
+ {
+ int64_t i = irep->pool[pool_no].u.i64;
+ if (i < INT32_MIN || INT32_MAX < i) {
+ cur += uint8_to_bin(IREP_TT_INT64, cur); /* data type */
+ cur += uint32_to_bin((uint32_t)((i>>32) & 0xffffffff), cur); /* i64 hi */
+ cur += uint32_to_bin((uint32_t)((i ) & 0xffffffff), cur); /* i64 lo */
+ }
+ else {
+ cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */
+ cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */
+ }
+ }
break;
#endif
+ case IREP_TT_INT32:
+ cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */
+ cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */
+ break;
- case MRB_TT_STRING:
- cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */
- str = irep->pool[pool_no];
+ case IREP_TT_BIGINT:
+ cur += uint8_to_bin(IREP_TT_BIGINT, cur); /* data type */
+ len = irep->pool[pool_no].u.str[0];
+ memcpy(cur, irep->pool[pool_no].u.str, (size_t)len+2);
+ cur += len+2;
+ *cur++ = '\0';
break;
- default:
- continue;
- }
+ case IREP_TT_FLOAT:
+ cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
+#ifndef MRB_NO_FLOAT
+ {
+ dump_float(mrb, cur,irep->pool[pool_no].u.f);
+ cur += sizeof(double);
+ }
+#else
+ cur += uint16_to_bin(0, cur); /* zero length */
+#endif
+ break;
- 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;
+ default: /* string */
+ cur += uint8_to_bin(IREP_TT_STR, cur); /* data type */
+ ptr = irep->pool[pool_no].u.str;
+ len = irep->pool[pool_no].tt>>2;
+ mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX);
+ cur += uint16_to_bin((uint16_t)len, cur); /* data length */
+ memcpy(cur, ptr, (size_t)len);
+ cur += len;
+ *cur++ = '\0';
+ break;
}
-
- 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)
+get_syms_block_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t size = 0;
int sym_no;
mrb_int len;
- size += sizeof(uint32_t); /* slen */
+ size += sizeof(uint16_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);
+ mrb_sym_name_len(mrb, irep->syms[sym_no], &len);
size += len + 1; /* sn(n) + null char */
}
}
@@ -235,19 +252,19 @@ get_syms_block_size(mrb_state *mrb, mrb_irep *irep)
}
static ptrdiff_t
-write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
+write_syms_block(mrb_state *mrb, const 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 */
+ cur += uint16_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);
+ name = mrb_sym_name_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 */
@@ -264,7 +281,7 @@ write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
}
static size_t
-get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep)
+get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep)
{
size_t size = 0;
@@ -276,7 +293,7 @@ get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep)
}
static size_t
-get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
+get_irep_record_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t size = 0;
int irep_no;
@@ -289,7 +306,7 @@ get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
}
static int
-write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
+write_irep_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
{
int i;
uint8_t *src = bin;
@@ -298,11 +315,6 @@ write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_rec
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);
@@ -350,7 +362,7 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
}
static int
-write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags)
+write_section_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags)
{
int result;
size_t rsize = 0;
@@ -366,126 +378,15 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p,
if (result != MRB_DUMP_OK) {
return result;
}
+ mrb_assert(rsize == get_irep_record_size(mrb, irep));
*len_p = cur - bin + rsize;
write_section_irep_header(mrb, *len_p, bin);
return MRB_DUMP_OK;
}
-static int
-write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
-{
- struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin;
-
- memcpy(header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(header->section_ident));
- uint32_to_bin((uint32_t)section_size, header->section_size);
-
- return MRB_DUMP_OK;
-}
-
-static size_t
-get_lineno_record_size(mrb_state *mrb, mrb_irep *irep)
-{
- size_t size = 0;
-
- size += sizeof(uint32_t); /* record size */
- size += sizeof(uint16_t); /* filename size */
- if (irep->filename) {
- size += strlen(irep->filename); /* filename */
- }
- size += sizeof(uint32_t); /* niseq */
- if (irep->lines) {
- size += sizeof(uint16_t) * irep->ilen; /* lineno */
- }
-
- return size;
-}
-
-static size_t
-write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
-{
- uint8_t *cur = bin;
- int iseq_no;
- size_t filename_len;
- ptrdiff_t diff;
-
- cur += sizeof(uint32_t); /* record size */
-
- if (irep->filename) {
- filename_len = strlen(irep->filename);
- }
- else {
- filename_len = 0;
- }
- mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX);
- cur += uint16_to_bin((uint16_t)filename_len, cur); /* filename size */
-
- if (filename_len) {
- memcpy(cur, irep->filename, filename_len);
- cur += filename_len; /* filename */
- }
-
- if (irep->lines) {
- mrb_assert_int_fit(size_t, irep->ilen, uint32_t, UINT32_MAX);
- cur += uint32_to_bin((uint32_t)(irep->ilen), cur); /* niseq */
- for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
- cur += uint16_to_bin(irep->lines[iseq_no], cur); /* opcode */
- }
- }
- else {
- cur += uint32_to_bin(0, cur); /* niseq */
- }
-
- diff = cur - bin;
- mrb_assert_int_fit(ptrdiff_t, diff, uint32_t, UINT32_MAX);
-
- uint32_to_bin((uint32_t)diff, bin); /* record size */
-
- mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
- return (size_t)diff;
-}
-
-static size_t
-write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
-{
- size_t rlen, size = 0;
- int i;
-
- rlen = write_lineno_record_1(mrb, irep, bin);
- bin += rlen;
- size += rlen;
- for (i=0; i<irep->rlen; i++) {
- rlen = write_lineno_record(mrb, irep, bin);
- bin += rlen;
- size += rlen;
- }
- return size;
-}
-
-static int
-write_section_lineno(mrb_state *mrb, mrb_irep *irep, uint8_t *bin)
-{
- size_t section_size = 0;
- size_t rlen = 0; /* size of irep record */
- uint8_t *cur = bin;
-
- if (mrb == NULL || bin == NULL) {
- return MRB_DUMP_INVALID_ARGUMENT;
- }
-
- cur += sizeof(struct rite_section_lineno_header);
- section_size += sizeof(struct rite_section_lineno_header);
-
- rlen = write_lineno_record(mrb, irep, cur);
- section_size += rlen;
-
- write_section_lineno_header(mrb, section_size, bin);
-
- return MRB_DUMP_OK;
-}
-
static size_t
-get_debug_record_size(mrb_state *mrb, mrb_irep *irep)
+get_debug_record_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t ret = 0;
uint16_t f_idx;
@@ -512,6 +413,10 @@ get_debug_record_size(mrb_state *mrb, mrb_irep *irep)
ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count);
break;
+ case mrb_debug_line_packed_map:
+ ret += (size_t)(file->line_entry_count);
+ break;
+
default: mrb_assert(0); break;
}
}
@@ -534,11 +439,11 @@ find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s)
}
static size_t
-get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp)
+get_filename_table_size(mrb_state *mrb, const 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;
+ const mrb_irep_debug_info *di = irep->debug_info;
int i;
mrb_assert(lp);
@@ -554,7 +459,7 @@ get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *
filenames[*lp - 1] = file->filename_sym;
/* filename */
- mrb_sym2name_len(mrb, file->filename_sym, &filename_len);
+ mrb_sym_name_len(mrb, file->filename_sym, &filename_len);
size += sizeof(uint16_t) + (size_t)filename_len;
}
}
@@ -565,7 +470,7 @@ get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *
}
static size_t
-write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
+write_debug_record_1(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
{
uint8_t *cur;
uint16_t f_idx;
@@ -606,6 +511,11 @@ write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const
}
} break;
+ case mrb_debug_line_packed_map: {
+ memcpy(cur, file->lines.packed_map, file->line_entry_count);
+ cur += file->line_entry_count;
+ } break;
+
default: mrb_assert(0); break;
}
}
@@ -619,7 +529,7 @@ write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const
}
static size_t
-write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
+write_debug_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
{
size_t size, len;
int irep_no;
@@ -637,7 +547,7 @@ write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const*
}
static int
-write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len)
+write_section_debug(mrb_state *mrb, const mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len)
{
size_t section_size = 0;
const uint8_t *bin = cur;
@@ -658,7 +568,7 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
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);
+ sym = mrb_sym_name_len(mrb, filenames[i], &sym_len);
mrb_assert(sym);
cur += uint16_to_bin((uint16_t)sym_len, cur);
memcpy(cur, sym, sym_len);
@@ -687,7 +597,7 @@ create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32
}
for (i = 0; i + 1 < irep->nlocals; ++i) {
- mrb_sym const name = irep->lv[i].name;
+ mrb_sym const name = irep->lv[i];
if (name == 0) continue;
if (find_filename_index(*syms, *syms_len, name) != -1) continue;
@@ -712,7 +622,7 @@ write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_
cur += uint32_to_bin(syms_len, cur);
for (i = 0; i < syms_len; ++i) {
- str = mrb_sym2name_len(mrb, syms[i], &str_len);
+ str = mrb_sym_name_len(mrb, syms[i], &str_len);
cur += uint16_to_bin((uint16_t)str_len, cur);
memcpy(cur, str, str_len);
cur += str_len;
@@ -730,16 +640,14 @@ write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym c
int i;
for (i = 0; i + 1 < irep->nlocals; ++i) {
- if (irep->lv[i].name == 0) {
+ if (irep->lv[i] == 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);
+ int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i]);
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);
}
}
@@ -753,12 +661,12 @@ write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym c
}
static size_t
-get_lv_record_size(mrb_state *mrb, mrb_irep *irep)
+get_lv_record_size(mrb_state *mrb, const mrb_irep *irep)
{
size_t ret = 0;
int i;
- ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1);
+ ret += sizeof(uint16_t) * (irep->nlocals - 1);
for (i = 0; i < irep->rlen; ++i) {
ret += get_lv_record_size(mrb, irep->reps[i]);
@@ -768,7 +676,7 @@ get_lv_record_size(mrb_state *mrb, mrb_irep *irep)
}
static size_t
-get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len)
+get_lv_section_size(mrb_state *mrb, const mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len)
{
size_t ret = 0, i;
@@ -776,7 +684,7 @@ get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_
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);
+ mrb_sym_name_len(mrb, syms[i], &str_len);
ret += str_len;
}
@@ -786,7 +694,7 @@ get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_
}
static int
-write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len)
+write_section_lv(mrb_state *mrb, const 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;
@@ -824,88 +732,53 @@ 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->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident));
+ memcpy(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version));
+ memcpy(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_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
-is_debug_info_defined(mrb_irep *irep)
+debug_info_defined_p(const mrb_irep *irep)
{
int i;
if (!irep->debug_info) return FALSE;
for (i=0; i<irep->rlen; i++) {
- if (!is_debug_info_defined(irep->reps[i])) return FALSE;
+ if (!debug_info_defined_p(irep->reps[i])) return FALSE;
}
return TRUE;
}
static mrb_bool
-is_lv_defined(mrb_irep *irep)
+lv_defined_p(const mrb_irep *irep)
{
int i;
if (irep->lv) { return TRUE; }
for (i = 0; i < irep->rlen; ++i) {
- if (is_lv_defined(irep->reps[i])) { return TRUE; }
+ 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)
+dump_irep(mrb_state *mrb, const 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 = is_debug_info_defined(irep), lv_defined = is_lv_defined(irep);
+ 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;
@@ -918,7 +791,7 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
section_irep_size += get_irep_record_size(mrb, irep);
/* DEBUG section size */
- if (flags & DUMP_DEBUG_INFO) {
+ if (flags & MRB_DUMP_DEBUG_INFO) {
if (debug_info_defined) {
section_lineno_size += sizeof(struct rite_section_debug_header);
/* filename table */
@@ -930,10 +803,6 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
section_lineno_size += get_debug_record_size(mrb, irep);
}
- else {
- section_lineno_size += sizeof(struct rite_section_lineno_header);
- section_lineno_size += get_lineno_record_size(mrb, irep);
- }
}
if (lv_defined) {
@@ -958,15 +827,12 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
sizeof(struct rite_binary_footer);
/* write DEBUG section */
- if (flags & DUMP_DEBUG_INFO) {
+ if (flags & MRB_DUMP_DEBUG_INFO) {
if (debug_info_defined) {
result = write_section_debug(mrb, irep, cur, filenames, filenames_len);
- }
- else {
- result = write_section_lineno(mrb, irep, cur);
- }
- if (result != MRB_DUMP_OK) {
- goto error_exit;
+ if (result != MRB_DUMP_OK) {
+ goto error_exit;
+ }
}
cur += section_lineno_size;
}
@@ -993,15 +859,15 @@ error_exit:
}
int
-mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
+mrb_dump_irep(mrb_state *mrb, const 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);
+ return dump_irep(mrb, irep, flags, bin, bin_size);
}
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
int
-mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp)
+mrb_dump_irep_binary(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE* fp)
{
uint8_t *bin = NULL;
size_t bin_size = 0;
@@ -1011,7 +877,7 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp)
return MRB_DUMP_INVALID_ARGUMENT;
}
- result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size);
+ result = dump_irep(mrb, irep, flags, &bin, &bin_size);
if (result == MRB_DUMP_OK) {
if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) {
result = MRB_DUMP_WRITE_FAULT;
@@ -1022,22 +888,8 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp)
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)
+mrb_dump_irep_cfunc(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname)
{
uint8_t *bin = NULL;
size_t bin_size = 0, bin_idx = 0;
@@ -1046,38 +898,20 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con
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 <stdint.h>\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) {
+ "%s\n"
+ "const uint8_t %s[] = {",
+ (flags & MRB_DUMP_STATIC) ? "static"
+ : "#ifdef __cplusplus\n"
+ "extern\n"
+ "#endif",
+ initname) < 0) {
mrb_free(mrb, bin);
return MRB_DUMP_WRITE_FAULT;
}
@@ -1103,4 +937,4 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con
return result;
}
-#endif /* MRB_DISABLE_STDIO */
+#endif /* MRB_NO_STDIO */
diff --git a/src/enum.c b/src/enum.c
index 03508953e..b95956715 100644
--- a/src/enum.c
+++ b/src/enum.c
@@ -14,25 +14,11 @@ enum_update_hash(mrb_state *mrb, mrb_value self)
mrb_int hash;
mrb_int index;
mrb_int hv;
- mrb_value item_hash;
- mrb_get_args(mrb, "iio", &hash, &index, &item_hash);
- if (mrb_fixnum_p(item_hash)) {
- hv = mrb_fixnum(item_hash);
- }
-#ifndef MRB_WITHOUT_FLOAT
- else if (mrb_float_p(item_hash)) {
- hv = (mrb_int)mrb_float(item_hash);
- }
-#endif
- else {
- mrb_raise(mrb, E_TYPE_ERROR, "can't calculate hash");
- /* not reached */
- hv = 0;
- }
- hash ^= (hv << (index % 16));
+ mrb_get_args(mrb, "iii", &hash, &index, &hv);
+ hash ^= ((uint32_t)hv << (index % 16));
- return mrb_fixnum_value(hash);
+ return mrb_int_value(mrb, hash);
}
void
@@ -40,5 +26,5 @@ 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));
+ mrb_define_module_function(mrb, enumerable, "__update_hash", enum_update_hash, MRB_ARGS_REQ(3));
}
diff --git a/src/error.c b/src/error.c
index 599612b97..7953deea7 100644
--- a/src/error.c
+++ b/src/error.c
@@ -13,10 +13,10 @@
#include <mruby/proc.h>
#include <mruby/string.h>
#include <mruby/variable.h>
-#include <mruby/debug.h>
#include <mruby/error.h>
#include <mruby/class.h>
#include <mruby/throw.h>
+#include <mruby/presym.h>
MRB_API mrb_value
mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
@@ -28,7 +28,7 @@ mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
MRB_API mrb_value
mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str)
{
- str = mrb_str_to_str(mrb, str);
+ mrb_to_str(mrb, str);
return mrb_obj_new(mrb, c, 1, &str);
}
@@ -44,11 +44,9 @@ 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);
+ if (mrb_get_args(mrb, "|o", &mesg) == 1) {
+ mrb_iv_set(mrb, exc, MRB_SYM(mesg), mesg);
}
return exc;
}
@@ -77,7 +75,7 @@ exc_exception(mrb_state *mrb, mrb_value self)
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);
+ mrb_iv_set(mrb, exc, MRB_SYM(mesg), a);
return exc;
}
@@ -93,7 +91,7 @@ exc_exception(mrb_state *mrb, mrb_value self)
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"));
+ mrb_value mesg = mrb_attr_get(mrb, exc, MRB_SYM(mesg));
struct RObject *p;
if (!mrb_string_p(mesg)) {
@@ -117,7 +115,7 @@ exc_to_s(mrb_state *mrb, mrb_value exc)
static mrb_value
exc_message(mrb_state *mrb, mrb_value exc)
{
- return mrb_funcall(mrb, exc, "to_s", 0);
+ return mrb_funcall_id(mrb, exc, MRB_SYM(to_s), 0);
}
/*
@@ -130,37 +128,13 @@ exc_message(mrb_state *mrb, mrb_value exc)
* returns message and class name.
*/
-static mrb_value
-exc_inspect(mrb_state *mrb, mrb_value exc)
+mrb_value
+mrb_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;
+ mrb_value mesg = mrb_attr_get(mrb, exc, MRB_SYM(mesg));
+ mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_obj_class(mrb, exc)));
+ mesg = mrb_obj_as_string(mrb, mesg);
+ return RSTRING_LEN(mesg) == 0 ? cname : mrb_format(mrb, "%v (%v)", mesg, cname);
}
void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc);
@@ -181,46 +155,18 @@ set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace)
p++;
}
}
- mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
+ mrb_iv_set(mrb, exc, MRB_SYM(backtrace), backtrace);
}
static mrb_value
exc_set_backtrace(mrb_state *mrb, mrb_value exc)
{
- mrb_value backtrace;
+ mrb_value backtrace = mrb_get_arg1(mrb);
- 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)
{
@@ -233,20 +179,15 @@ mrb_exc_set(mrb_state *mrb, mrb_value exc)
(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);
+ if (!mrb->gc.out_of_memory && !mrb_frozen_p(mrb->exc)) {
mrb_keep_backtrace(mrb, exc);
}
}
}
-MRB_API mrb_noreturn void
-mrb_exc_raise(mrb_state *mrb, mrb_value exc)
+static mrb_noreturn void
+exc_throw(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();
@@ -255,64 +196,182 @@ mrb_exc_raise(mrb_state *mrb, mrb_value exc)
}
MRB_API mrb_noreturn void
+mrb_exc_raise(mrb_state *mrb, mrb_value exc)
+{
+ if (mrb_break_p(exc)) {
+ mrb->exc = mrb_obj_ptr(exc);
+ }
+ else {
+ 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);
+ }
+ exc_throw(mrb, exc);
+}
+
+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)));
}
+/*
+ * <code>vsprintf</code> like formatting.
+ *
+ * The syntax of a format sequence is as follows.
+ *
+ * %[modifier]specifier
+ *
+ * The modifiers are:
+ *
+ * ----------+------------------------------------------------------------
+ * Modifier | Meaning
+ * ----------+------------------------------------------------------------
+ * ! | Convert to string by corresponding `inspect` instead of
+ * | corresponding `to_s`.
+ * ----------+------------------------------------------------------------
+ *
+ * The specifiers are:
+ *
+ * ----------+----------------+--------------------------------------------
+ * Specifier | Argument Type | Note
+ * ----------+----------------+--------------------------------------------
+ * c | char |
+ * d | int |
+ * f | mrb_float |
+ * i | mrb_int |
+ * l | char*, size_t | Arguments are string and length.
+ * n | mrb_sym |
+ * s | char* | Argument is NUL terminated string.
+ * t | mrb_value | Convert to type (class) of object.
+ * v,S | mrb_value |
+ * C | struct RClass* |
+ * T | mrb_value | Convert to real type (class) of object.
+ * Y | mrb_value | Same as `!v` if argument is `true`, `false`
+ * | | or `nil`, otherwise same as `T`.
+ * % | - | Convert to percent sign itself (no argument
+ * | | taken).
+ * ----------+----------------+--------------------------------------------
+ */
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);
+ const char *chars, *p = format, *b = format, *e;
+ char ch;
+ size_t len;
+ mrb_int i;
+ struct RClass *cls;
+ mrb_bool inspect = FALSE;
+ mrb_value result = mrb_str_new_capa(mrb, 128), obj, str;
int ai = mrb_gc_arena_save(mrb);
while (*p) {
const char c = *p++;
-
+ e = 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;
+ if (*p == '!') {
+ inspect = TRUE;
+ ++p;
}
- else {
- break;
+ if (!*p) break;
+ switch (*p) {
+ case 'c':
+ ch = (char)va_arg(ap, int);
+ chars = &ch;
+ len = 1;
+ goto L_cat;
+ case 'd': case 'i':
+#if MRB_INT_MAX < INT_MAX
+ i = (mrb_int)va_arg(ap, int);
+#else
+ i = *p == 'd' ? (mrb_int)va_arg(ap, int) : va_arg(ap, mrb_int);
+#endif
+ obj = mrb_fixnum_value(i);
+ goto L_cat_obj;
+#ifndef MRB_NO_FLOAT
+ case 'f':
+ obj = mrb_float_value(mrb, (mrb_float)va_arg(ap, double));
+ goto L_cat_obj;
+#endif
+ case 'l':
+ chars = va_arg(ap, char*);
+ len = va_arg(ap, size_t);
+ L_cat:
+ if (inspect) {
+ obj = mrb_str_new(mrb, chars, len);
+ goto L_cat_obj;
+ }
+ L_cat_plain:
+ mrb_str_cat(mrb, result, b, e - b - 1);
+ mrb_str_cat(mrb, result, chars, len);
+ b = ++p;
+ mrb_gc_arena_restore(mrb, ai);
+ break;
+ case 'n':
+#if UINT32_MAX < INT_MAX
+ obj = mrb_symbol_value((mrb_sym)va_arg(ap, int));
+#else
+ obj = mrb_symbol_value(va_arg(ap, mrb_sym));
+#endif
+ goto L_cat_obj;
+ case 's':
+ chars = va_arg(ap, char*);
+ len = strlen(chars);
+ goto L_cat;
+ case 't':
+ cls = mrb_class(mrb, va_arg(ap, mrb_value));
+ goto L_cat_class;
+ case 'v': case 'S':
+ obj = va_arg(ap, mrb_value);
+ L_cat_obj:
+ str = (inspect ? mrb_inspect : mrb_obj_as_string)(mrb, obj);
+ if (mrb_type(str) != MRB_TT_STRING) {
+ chars = "void (no string conversion)";
+ len = strlen(chars);
+ }
+ else {
+ chars = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+ }
+ goto L_cat_plain;
+ case 'C':
+ cls = va_arg(ap, struct RClass*);
+ L_cat_class:
+ obj = mrb_obj_value(cls);
+ goto L_cat_obj;
+ case 'T':
+ obj = va_arg(ap, mrb_value);
+ L_cat_real_class_of:
+ cls = mrb_obj_class(mrb, obj);
+ goto L_cat_class;
+ case 'Y':
+ obj = va_arg(ap, mrb_value);
+ if (!mrb_test(obj) || mrb_true_p(obj)) {
+ inspect = TRUE;
+ goto L_cat_obj;
+ }
+ else {
+ goto L_cat_real_class_of;
+ }
+ case '%':
+ L_cat_current:
+ chars = p;
+ len = 1;
+ goto L_cat_plain;
+ default:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p);
}
}
- 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;
+ else if (c == '\\') {
+ if (!*p) break;
+ goto L_cat_current;
- 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_str_cat(mrb, result, b, p - b);
+ return result;
}
MRB_API mrb_value
@@ -368,7 +427,7 @@ mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
MRB_API void
mrb_warn(mrb_state *mrb, const char *fmt, ...)
{
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
va_list ap;
mrb_value str;
@@ -376,6 +435,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
str = mrb_vformat(mrb, fmt, ap);
fputs("warning: ", stderr);
fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
+ putc('\n', stderr);
va_end(ap);
#endif
}
@@ -383,7 +443,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
MRB_API mrb_noreturn void
mrb_bug(mrb_state *mrb, const char *fmt, ...)
{
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
va_list ap;
mrb_value str;
@@ -421,7 +481,7 @@ mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv)
n = 1;
exception_call:
{
- mrb_sym exc = mrb_intern_lit(mrb, "exception");
+ mrb_sym exc = MRB_SYM(exception);
if (mrb_respond_to(mrb, argv[0], exc)) {
mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1);
}
@@ -433,7 +493,7 @@ exception_call:
break;
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc));
+ mrb_argnum_error(mrb, argc, 0, 3);
break;
}
if (argc > 0) {
@@ -483,6 +543,93 @@ mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt,
mrb_exc_raise(mrb, exc);
}
+MRB_API mrb_noreturn void
+mrb_frozen_error(mrb_state *mrb, void *frozen_obj)
+{
+ mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %t", mrb_obj_value(frozen_obj));
+}
+
+MRB_API mrb_noreturn void
+mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max)
+{
+#define FMT(exp) "wrong number of arguments (given %i, expected " exp ")"
+ if (min == max)
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d"), argc, min);
+ else if (max < 0)
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d+"), argc, min);
+ else
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d..%d"), argc, min, max);
+#undef FMT
+}
+
+void mrb_core_init_printabort(void);
+
+int
+mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ int err = 1;
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ body(mrb, opaque);
+ err = 0;
+ } MRB_CATCH(&c_jmp) {
+ if (mrb->exc) {
+ mrb_p(mrb, mrb_obj_value(mrb->exc));
+ mrb->exc = NULL;
+ }
+ else {
+ mrb_core_init_printabort();
+ }
+ } MRB_END_EXC(&c_jmp);
+
+ mrb->jmp = prev_jmp;
+
+ return err;
+}
+
+mrb_noreturn void
+mrb_core_init_abort(mrb_state *mrb)
+{
+ mrb->exc = NULL;
+ exc_throw(mrb, mrb_nil_value());
+}
+
+void
+mrb_protect_atexit(mrb_state *mrb)
+{
+ if (mrb->atexit_stack_len > 0) {
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ for (int i = mrb->atexit_stack_len; i > 0; --i) {
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ mrb->atexit_stack[i - 1](mrb);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ /* ignore atexit errors */
+ } MRB_END_EXC(&c_jmp);
+ }
+#ifndef MRB_FIXED_STATE_ATEXIT_STACK
+ mrb_free(mrb, mrb->atexit_stack);
+#endif
+ mrb->jmp = prev_jmp;
+ }
+}
+
+mrb_noreturn void
+mrb_raise_nomemory(mrb_state *mrb)
+{
+ if (mrb->nomem_err) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
+ }
+ else {
+ mrb_core_init_abort(mrb);
+ }
+}
+
void
mrb_init_exception(mrb_state *mrb)
{
@@ -490,12 +637,12 @@ mrb_init_exception(mrb_state *mrb)
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_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_OPT(1));
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, "inspect", mrb_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));
@@ -504,11 +651,11 @@ mrb_init_exception(mrb_state *mrb)
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"));
+ mrb->stack_err = mrb_obj_ptr(mrb_exc_new_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"));
+ mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_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"));
+ mrb->arena_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "arena overflow error"));
#endif
}
diff --git a/src/etc.c b/src/etc.c
index 12d948a55..a5c48bbe3 100644
--- a/src/etc.c
+++ b/src/etc.c
@@ -1,5 +1,5 @@
/*
-** etc.c -
+** etc.c
**
** See Copyright Notice in mruby.h
*/
@@ -8,15 +8,14 @@
#include <mruby/string.h>
#include <mruby/data.h>
#include <mruby/class.h>
-#include <mruby/re.h>
-#include <mruby/irep.h>
+#include <mruby/numeric.h>
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 = MRB_OBJ_ALLOC(mrb, MRB_TT_DATA, klass);
data->data = ptr;
data->type = type;
@@ -26,21 +25,19 @@ mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb
MRB_API void
mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
{
- if (mrb_type(obj) != MRB_TT_DATA) {
+ if (!mrb_data_p(obj)) {
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));
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
+ t2->struct_name, 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_raisef(mrb, E_TYPE_ERROR, "uninitialized %t (expected %s)",
+ obj, type->struct_name);
}
}
}
@@ -48,7 +45,7 @@ mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
MRB_API void*
mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
{
- if (mrb_type(obj) != MRB_TT_DATA) {
+ if (!mrb_data_p(obj)) {
return NULL;
}
if (DATA_TYPE(obj) != type) {
@@ -67,41 +64,17 @@ mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
MRB_API mrb_sym
mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
{
- mrb_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;
+ if (mrb_symbol_p(name)) return mrb_symbol(name);
+ if (mrb_string_p(name)) return mrb_intern_str(mrb, name);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a symbol nor a string", name);
+ return 0; /* not reached */
}
-MRB_API mrb_int
-#ifdef MRB_WITHOUT_FLOAT
-mrb_fixnum_id(mrb_int f)
-#else
-mrb_float_id(mrb_float f)
-#endif
+static mrb_int
+make_num_id(const char *p, size_t len)
{
- 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++;
@@ -112,6 +85,22 @@ mrb_float_id(mrb_float f)
}
MRB_API mrb_int
+mrb_int_id(mrb_int n)
+{
+ return make_num_id((const char*)&n, sizeof(n));
+}
+
+#ifndef MRB_NO_FLOAT
+MRB_API mrb_int
+mrb_float_id(mrb_float f)
+{
+ /* normalize -0.0 to 0.0 */
+ if (f == 0) f = 0.0;
+ return make_num_id((const char*)&f, sizeof(f));
+}
+#endif
+
+MRB_API mrb_int
mrb_obj_id(mrb_value obj)
{
mrb_int tt = mrb_type(obj);
@@ -125,17 +114,16 @@ mrb_obj_id(mrb_value obj)
return MakeID(0); /* not define */
case MRB_TT_FALSE:
if (mrb_nil_p(obj))
- return MakeID(1);
- return MakeID(0);
+ return MakeID(4);
+ else
+ return MakeID(0);
case MRB_TT_TRUE:
- return MakeID(1);
+ return MakeID(2);
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_INTEGER:
+ return MakeID(mrb_int_id(mrb_integer(obj)));
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return MakeID(mrb_float_id(mrb_float(obj)));
#endif
@@ -150,7 +138,6 @@ mrb_obj_id(mrb_value obj)
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:
@@ -158,56 +145,53 @@ mrb_obj_id(mrb_value obj)
}
}
+#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT)
+#define mrb_xxx_boxing_cptr_value mrb_nan_boxing_cptr_value
+#endif
+
#ifdef MRB_WORD_BOXING
-#ifndef MRB_WITHOUT_FLOAT
+#define mrb_xxx_boxing_cptr_value mrb_word_boxing_cptr_value
+
+#ifndef MRB_NO_FLOAT
MRB_API mrb_value
mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
{
- mrb_value v;
+ union mrb_value_ v;
- v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
- v.value.fp->f = f;
- return v;
+ v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
+ v.fp->f = f;
+ MRB_SET_FROZEN_FLAG(v.bp);
+ return v.value;
}
+#endif /* MRB_NO_FLOAT */
MRB_API mrb_value
-mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f)
+mrb_word_boxing_int_value(mrb_state *mrb, mrb_int n)
{
- 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);
+ if (FIXABLE(n)) return mrb_fixnum_value(n);
+ else {
+ union mrb_value_ v;
+
+ v.p = mrb_obj_alloc(mrb, MRB_TT_INTEGER, mrb->integer_class);
+ v.ip->i = n;
+ MRB_SET_FROZEN_FLAG(v.ip);
+ return v.value;
+ }
}
-#endif /* MRB_WITHOUT_FLOAT */
+#endif /* MRB_WORD_BOXING */
+#if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_64BIT))
MRB_API mrb_value
-mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
+mrb_xxx_boxing_cptr_value(mrb_state *mrb, void *p)
{
mrb_value v;
+ struct RCptr *cptr = MRB_OBJ_ALLOC(mrb, MRB_TT_CPTR, mrb->object_class);
- v.value.p = mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class);
- v.value.vp->p = p;
+ SET_OBJ_VALUE(v, cptr);
+ cptr->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;
-}
+#endif
#if defined _MSC_VER && _MSC_VER < 1900
diff --git a/src/ext/.gitkeep b/src/ext/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
--- a/src/ext/.gitkeep
+++ /dev/null
diff --git a/src/fmt_fp.c b/src/fmt_fp.c
index 783cc6c02..43daf2307 100644
--- a/src/fmt_fp.c
+++ b/src/fmt_fp.c
@@ -1,388 +1,364 @@
-#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.
+#include <mruby.h>
+#include <string.h>
+#include <stdlib.h>
-Copyright (c) 2005-2014 Rich Felker, et al.
+#ifndef MRB_NO_FLOAT
+/***********************************************************************
-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:
+ Routine for converting a single-precision
+ floating point number into a string.
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
+ The code in this function was inspired from Fred Bayer's pdouble.c.
+ Since pdouble.c was released as Public Domain, I'm releasing this
+ code as public domain as well.
-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.
+ Dave Hylands
-*/
+ The original code can be found in https://github.com/dhylands/format-float
+***********************************************************************/
-#include <limits.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-#include <float.h>
-#include <ctype.h>
+/***********************************************************************
-#include <mruby.h>
-#include <mruby/string.h>
+ I modified the routine for mruby:
-struct fmt_args {
- mrb_state *mrb;
- mrb_value str;
-};
+ * support `double`
+ * support `#` (alt_form) modifier
-#define MAX(a,b) ((a)>(b) ? (a) : (b))
-#define MIN(a,b) ((a)<(b) ? (a) : (b))
+ My modifications in this file are also placed in the public domain.
-/* Convenient bit representation for modifier flags, which all fall
- * within 31 codepoints of the space character. */
+ Matz (Yukihiro Matsumoto)
-#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);
-}
+#include <math.h>
-#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);
-}
+#ifdef MRB_USE_FLOAT32
-static const char xdigits[16] = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
-};
+// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
+// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
+// exponent is stored with a bias of 127.
+// The min and max floats are on the order of 1x10^37 and 1x10^-37
-static char*
-fmt_u(uint32_t x, char *s)
-{
- for (; x; x /= 10) *--s = '0' + x % 10;
- return s;
-}
+#define FLT_DECEXP 32
+#define FLT_ROUND_TO_ONE 0.9999995F
+#define FLT_MIN_BUF_SIZE 6 // -9e+99
-/* 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
+#else
-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;
- }
+// 1 sign bit, 11 exponent bits, and 52 mantissa bits.
- y = frexp((double)y, &e2) * 2;
- if (y) e2--;
+#define FLT_DECEXP 256
+#define FLT_ROUND_TO_ONE 0.999999999995
+#define FLT_MIN_BUF_SIZE 7 // -9e+199
- if ((t|32)=='a') {
- long double round = 8.0;
- ptrdiff_t re;
+#endif /* MRB_USE_FLOAT32 */
- if (t&32) prefix += 9;
- pl += 2;
+static const mrb_float g_pos_pow[] = {
+#ifndef MRB_USE_FLOAT32
+ 1e256, 1e128, 1e64,
+#endif
+ 1e32, 1e16, 1e8, 1e4, 1e2, 1e1
+};
+static const mrb_float g_neg_pow[] = {
+#ifndef MRB_USE_FLOAT32
+ 1e-256, 1e-128, 1e-64,
+#endif
+ 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1
+};
- if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
- else re=LDBL_MANT_DIG/4-1-p;
+/*
+ * mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign)
+ *
+ * fmt: should be one of 'e', 'E', 'f', 'F', 'g', or 'G'. (|0x80 for '#')
+ * prec: is the precision (as specified in printf)
+ * sign: should be '\0', '+', or ' ' ('\0' is the normal one - only print
+ * a sign if ```f``` is negative. Anything else is printed as the
+ * sign character for positive numbers.
+ */
+
+int
+mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
+ char *s = buf;
+ int buf_remaining = buf_size - 1;
+ int alt_form = 0;
+
+ if ((uint8_t)fmt & 0x80) {
+ fmt &= 0x7f; /* turn off alt_form flag */
+ alt_form = 1;
+ }
+ if (buf_size <= FLT_MIN_BUF_SIZE) {
+ // Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating
+ // null.
- if (re) {
- while (re--) round*=16;
- if (*prefix=='-') {
- y=-y;
- y-=round;
- y+=round;
- y=-y;
- }
- else {
- y+=round;
- y-=round;
- }
+ if (buf_size >= 2) {
+ *s++ = '?';
+ }
+ if (buf_size >= 1) {
+ *s++ = '\0';
+ }
+ return buf_size >= 2;
+ }
+ if (signbit(f)) {
+ *s++ = '-';
+ f = -f;
+ } else if (sign) {
+ *s++ = sign;
+ }
+ buf_remaining -= (s - buf); // Adjust for sign
+
+ {
+ char uc = fmt & 0x20;
+ if (isinf(f)) {
+ *s++ = 'I' ^ uc;
+ *s++ = 'N' ^ uc;
+ *s++ = 'F' ^ uc;
+ goto ret;
+ } else if (isnan(f)) {
+ *s++ = 'N' ^ uc;
+ *s++ = 'A' ^ uc;
+ *s++ = 'N' ^ uc;
+ ret:
+ *s = '\0';
+ return s - buf;
}
+ }
- 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 (prec < 0) {
+ prec = 6;
}
- if (p<0) p=6;
+ char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
+ fmt |= 0x20; // Force fmt to be lowercase
+ char org_fmt = fmt;
+ if (fmt == 'g' && prec == 0) {
+ prec = 1;
+ }
+ int e, e1;
+ int dec = 0;
+ char e_sign = '\0';
+ int num_digits = 0;
+ const mrb_float *pos_pow = g_pos_pow;
+ const mrb_float *neg_pow = g_neg_pow;
+
+ if (f == 0.0) {
+ e = 0;
+ if (fmt == 'e') {
+ e_sign = '+';
+ } else if (fmt == 'f') {
+ num_digits = prec + 1;
+ }
+ } else if (f < 1.0) { // f < 1.0
+ char first_dig = '0';
+ if (f >= FLT_ROUND_TO_ONE) {
+ first_dig = '1';
+ }
- if (y) y *= 268435456.0, e2-=28;
+ // Build negative exponent
+ for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
+ if (*neg_pow > f) {
+ e += e1;
+ f *= *pos_pow;
+ }
+ }
+ char e_sign_char = '-';
+ if (f < 1.0) {
+ if (f >= FLT_ROUND_TO_ONE) {
+ f = 1.0;
+ if (e == 0) {
+ e_sign_char = '+';
+ }
+ } else {
+ e++;
+ f *= 10.0;
+ }
+ }
- if (e2<0) a=r=z=big;
- else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
+ // If the user specified 'g' format, and e is <= 4, then we'll switch
+ // to the fixed format ('f')
- do {
- *z = (uint32_t)y;
- y = 1000000000*(y-*z++);
- } while (y);
+ if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
+ fmt = 'f';
+ dec = -1;
+ *s++ = first_dig;
- 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<<sh)+carry;
- *d = x % 1000000000;
- carry = (uint32_t)(x / 1000000000);
+ if (org_fmt == 'g') {
+ prec += (e - 1);
+ }
+ // truncate precision to prevent buffer overflow
+ if (prec + 2 > buf_remaining) {
+ prec = buf_remaining - 2;
+ }
+ num_digits = prec;
+ if (num_digits || alt_form) {
+ *s++ = '.';
+ while (--e && num_digits) {
+ *s++ = '0';
+ num_digits--;
+ }
+ }
+ } else {
+ // For e & g formats, we'll be printing the exponent, so set the
+ // sign.
+ e_sign = e_sign_char;
+ dec = 0;
+
+ if (prec > (buf_remaining - FLT_MIN_BUF_SIZE)) {
+ prec = buf_remaining - FLT_MIN_BUF_SIZE;
+ if (fmt == 'g') {
+ prec++;
+ }
+ }
}
- if (carry) *--a = carry;
- while (z>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<z; d++) {
- uint32_t rm = *d & ((1<<sh)-1);
- *d = (*d>>sh) + carry;
- carry = (1000000000>>sh) * rm;
+ } else {
+ // Build positive exponent
+ for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
+ if (*pos_pow <= f) {
+ e += e1;
+ f *= *neg_pow;
+ }
}
- 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<z) for (i=10, e=9*(int)(r-a); *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<i/2) small=0.5;
- else if (x==i/2 && d+1==z) small=1.0;
- else small=1.5;
- if (pl && *prefix=='-') round*=-1, small*=-1;
- *d -= x;
- /* Decide whether to round by probing round+small */
- if (round+small != round) {
- *d = *d + i;
- while (*d > 999999999) {
- *d--=0;
- if (d<a) *--a=0;
- (*d)++;
+ // If the user specified fixed format (fmt == 'f') and e makes the
+ // number too big to fit into the available buffer, then we'll
+ // switch to the 'e' format.
+
+ if (fmt == 'f') {
+ if (e >= buf_remaining) {
+ fmt = 'e';
+ } else if ((e + prec + 2) > buf_remaining) {
+ prec = buf_remaining - e - 2;
+ if (prec < 0) {
+ // This means no decimal point, so we can add one back
+ // for the decimal.
+ prec++;
}
- for (i=10, e=9*(int)(r-a); *a>=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;
+ if (fmt == 'e' && prec > (buf_remaining - 6)) {
+ prec = buf_remaining - 6;
}
- else {
- t-=2;
- p--;
+ // If the user specified 'g' format, and e is < prec, then we'll switch
+ // to the fixed format.
+
+ if (fmt == 'g' && e < prec) {
+ fmt = 'f';
+ prec -= (e + 1);
}
- 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));
+ if (fmt == 'f') {
+ dec = e;
+ num_digits = prec + e + 1;
+ } else {
+ e_sign = '+';
}
}
- 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;
+ if (prec < 0) {
+ // This can happen when the prec is trimmed to prevent buffer overflow
+ prec = 0;
}
- 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);
+ // We now have f as a floating point number between >= 1 and < 10
+ // (or equal to zero), and e contains the absolute value of the power of
+ // 10 exponent. and (dec + 1) == the number of dgits before the decimal.
+
+ // For e, prec is # digits after the decimal
+ // For f, prec is # digits after the decimal
+ // For g, prec is the max number of significant digits
+ //
+ // For e & g there will be a single digit before the decimal
+ // for f there will be e digits before the decimal
+
+ if (fmt == 'e') {
+ num_digits = prec + 1;
+ } else if (fmt == 'g') {
+ if (prec == 0) {
+ prec = 1;
}
- if (p || (fl&ALT_FORM)) out(f, ".", 1);
- for (; d<z && p>0; 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);
+ num_digits = prec;
}
- else {
- if (z<=a) z=a+1;
- for (d=a; d<z && p>=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;
+ // Print the digits of the mantissa
+ for (int i = 0; i < num_digits; ++i, --dec) {
+ int8_t d = f;
+ *s++ = '0' + d;
+ if (dec == 0 && (prec > 0 || alt_form)) {
+ *s++ = '.';
+ }
+ f -= (mrb_float)d;
+ f *= 10.0;
}
- ++fmt;
- if (*fmt == '.') {
- ++fmt;
- for (p = 0; ISDIGIT(*fmt); ++fmt) {
- p = 10 * p + (*fmt - '0');
+ // Round
+ if (f >= 5.0) {
+ char *rs = s;
+ rs--;
+ while (1) {
+ if (*rs == '.') {
+ rs--;
+ continue;
+ }
+ if (*rs < '0' || *rs > '9') {
+ // + or -
+ rs++; // So we sit on the digit to the right of the sign
+ break;
+ }
+ if (*rs < '9') {
+ (*rs)++;
+ break;
+ }
+ *rs = '0';
+ if (rs == buf) {
+ break;
+ }
+ rs--;
+ }
+ if (*rs == '0') {
+ // We need to insert a 1
+ if (rs[1] == '.' && fmt != 'f') {
+ // We're going to round 9.99 to 10.00
+ // Move the decimal point
+ rs[0] = '.';
+ rs[1] = '0';
+ if (e_sign == '-') {
+ e--;
+ } else {
+ e++;
+ }
+ }
+ s++;
+ char *ss = s;
+ while (ss > rs) {
+ *ss = ss[-1];
+ ss--;
+ }
+ *rs = '1';
+ if (f < 1.0 && fmt == 'f') {
+ // We rounded up to 1.0
+ prec--;
+ }
}
- }
- 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;
+ if (org_fmt == 'g' && prec > 0 && !alt_form) {
+ // Remove trailing zeros and a trailing decimal point
+ while (s[-1] == '0') {
+ s--;
+ }
+ if (s[-1] == '.') {
+ s--;
+ }
}
-}
-
-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");
+ // Append the exponent
+ if (e_sign) {
+ *s++ = e_char;
+ *s++ = e_sign;
+ if (e >= 100) {
+ *s++ = '0' + (e / 100);
+ e %= 100;
+ }
+ *s++ = '0' + (e / 10);
+ *s++ = '0' + (e % 10);
}
- return f.str;
-}
-#else /* MRB_DISABLE_STDIO */
-#include <mruby.h>
-#include <stdio.h>
-
-mrb_value
-mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
-{
- char buf[24];
+ *s = '\0';
- snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
- return mrb_str_new_cstr(mrb, buf);
+ return s - buf;
}
-#endif /* MRB_DISABLE_STDIO */
#endif
diff --git a/src/gc.c b/src/gc.c
index e429603dd..bcd0b29b6 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -6,10 +6,14 @@
#include <string.h>
#include <stdlib.h>
+#ifdef MRB_USE_MALLOC_TRIM
+#include <malloc.h>
+#endif
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/data.h>
+#include <mruby/istruct.h>
#include <mruby/hash.h>
#include <mruby/proc.h>
#include <mruby/range.h>
@@ -18,6 +22,7 @@
#include <mruby/gc.h>
#include <mruby/error.h>
#include <mruby/throw.h>
+#include <mruby/presym.h>
/*
= Tri-color Incremental Garbage Collection
@@ -34,9 +39,14 @@
* Gray - Marked, But the child objects are unmarked.
* Black - Marked, the child objects are also marked.
+ Extra color
+
+ * Red - Static (ROM object) no need to be collected.
+ - All child objects should be Red as well.
+
== Two White Types
- There're two white color types in a flip-flop fashion: White-A and White-B,
+ There are 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).
@@ -98,8 +108,14 @@ struct free_obj {
struct RBasic *next;
};
+struct RVALUE_initializer {
+ MRB_OBJECT_HEADER;
+ char padding[sizeof(void*) * 4 - sizeof(uint32_t)];
+};
+
typedef struct {
union {
+ struct RVALUE_initializer init; /* must be first member to ensure initialization */
struct free_obj free;
struct RBasic basic;
struct RObject object;
@@ -109,12 +125,14 @@ typedef struct {
struct RHash hash;
struct RRange range;
struct RData data;
+ struct RIStruct istruct;
struct RProc proc;
struct REnv env;
+ struct RFiber fiber;
struct RException exc;
struct RBreak brk;
#ifdef MRB_WORD_BOXING
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
struct RFloat floatv;
#endif
struct RCptr cptr;
@@ -182,8 +200,10 @@ gettimeofday_time(void)
#define GC_WHITE_A 1
#define GC_WHITE_B (1 << 1)
#define GC_BLACK (1 << 2)
+#define GC_RED MRB_GC_RED
#define GC_WHITES (GC_WHITE_A | GC_WHITE_B)
#define GC_COLOR_MASK 7
+mrb_static_assert1(MRB_GC_RED <= GC_COLOR_MASK);
#define paint_gray(o) ((o)->color = GC_GRAY)
#define paint_black(o) ((o)->color = GC_BLACK)
@@ -191,13 +211,16 @@ gettimeofday_time(void)
#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 is_black(o) ((o)->color == GC_BLACK)
+#define is_red(o) ((o)->color == GC_RED)
#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_noreturn void mrb_raise_nomemory(mrb_state *mrb);
+
MRB_API void*
mrb_realloc_simple(mrb_state *mrb, void *p, size_t len)
{
@@ -220,14 +243,8 @@ mrb_realloc(mrb_state *mrb, void *p, size_t len)
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));
- }
+ mrb->gc.out_of_memory = TRUE;
+ mrb_raise_nomemory(mrb);
}
else {
mrb->gc.out_of_memory = FALSE;
@@ -274,9 +291,37 @@ mrb_free(mrb_state *mrb, void *p)
(mrb->allocf)(mrb, p, 0, mrb->allocf_ud);
}
+MRB_API void*
+mrb_alloca(mrb_state *mrb, size_t size)
+{
+ struct RString *s;
+ s = MRB_OBJ_ALLOC(mrb, MRB_TT_STRING, mrb->string_class);
+ return s->as.heap.ptr = (char*)mrb_malloc(mrb, size);
+}
+
+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) {
- return is_dead(&mrb->gc, object);
+ mrb_gc *gc = &mrb->gc;
+ if (!heap_p(gc, object)) return TRUE;
+ return is_dead(gc, object);
}
static void
@@ -376,7 +421,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
static void obj_free(mrb_state *mrb, struct RBasic *obj, int end);
-void
+static void
free_heap(mrb_state *mrb, mrb_gc *gc)
{
mrb_heap_page *page = gc->heaps;
@@ -430,7 +475,7 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj)
gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj));
}
-#define GC_ROOT_NAME "_gc_root_"
+#define GC_ROOT_SYM MRB_SYM(_gc_root_)
/* mrb_gc_register() keeps the object from GC.
@@ -443,10 +488,13 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj)
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);
+ mrb_sym root;
+ mrb_value table;
- if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) {
+ if (mrb_immediate_p(obj)) return;
+ root = GC_ROOT_SYM;
+ table = mrb_gv_get(mrb, root);
+ if (mrb_nil_p(table) || !mrb_array_p(table)) {
table = mrb_ary_new(mrb);
mrb_gv_set(mrb, root, table);
}
@@ -457,20 +505,23 @@ mrb_gc_register(mrb_state *mrb, mrb_value obj)
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);
+ mrb_sym root;
+ mrb_value table;
struct RArray *a;
mrb_int i;
+ if (mrb_immediate_p(obj)) return;
+ root = GC_ROOT_SYM;
+ table = mrb_gv_get(mrb, root);
if (mrb_nil_p(table)) return;
- if (mrb_type(table) != MRB_TT_ARRAY) {
+ if (!mrb_array_p(table)) {
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)) {
+ if (mrb_ptr(ARY_PTR(a)[i]) == mrb_ptr(obj)) {
mrb_int len = ARY_LEN(a)-1;
mrb_value *ptr = ARY_PTR(a);
@@ -485,7 +536,7 @@ 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 } } };
+ static const RVALUE RVALUE_zero = { { { NULL, NULL, MRB_TT_FALSE } } };
mrb_gc *gc = &mrb->gc;
if (cls) {
@@ -506,9 +557,12 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
ttype != MRB_TT_ICLASS &&
ttype != MRB_TT_ENV &&
ttype != tt) {
- mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %S", mrb_obj_value(cls));
+ mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls);
}
}
+ if (ttype <= MRB_TT_FREE) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C (type %d)", cls, (int)ttype);
+ }
#ifdef MRB_GC_STRESS
mrb_full_gc(mrb);
@@ -548,11 +602,11 @@ add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
gc->gray_list = obj;
}
-static int
+static mrb_int
ci_nregs(mrb_callinfo *ci)
{
- struct RProc *p = ci->proc;
- int n = 0;
+ const struct RProc *p = ci->proc;
+ mrb_int n = 0;
if (!p) {
if (ci->argc < 0) return 3;
@@ -577,11 +631,14 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c)
size_t e;
mrb_value nil;
- if (c->stack == NULL) return;
- e = c->stack - c->stbase;
+ if (c->stbase == NULL) return;
if (c->ci) {
+ e = (c->ci->stack ? c->ci->stack - c->stbase : 0);
e += ci_nregs(c->ci);
}
+ else {
+ e = 0;
+ }
if (c->stbase + e > c->stend) e = c->stend - c->stbase;
for (i=0; i<e; i++) {
mrb_value v = c->stbase[i];
@@ -600,7 +657,6 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c)
static void
mark_context(mrb_state *mrb, struct mrb_context *c)
{
- int i;
mrb_callinfo *ci;
start:
@@ -612,15 +668,10 @@ mark_context(mrb_state *mrb, struct mrb_context *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);
+ mrb_gc_mark(mrb, (struct RBasic*)ci->u.target_class);
}
}
- /* mark ensure stack */
- for (i=0; i<c->eidx; i++) {
- mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]);
- }
/* mark fibers */
mrb_gc_mark(mrb, (struct RBasic*)c->fib);
if (c->prev) {
@@ -634,7 +685,6 @@ 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:
@@ -677,10 +727,10 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
struct REnv *e = (struct REnv*)obj;
mrb_int i, len;
- if (MRB_ENV_STACK_SHARED_P(e) && e->cxt && e->cxt->fib) {
+ if (MRB_ENV_ONSTACK_P(e) && e->cxt && e->cxt->fib) {
mrb_gc_mark(mrb, (struct RBasic*)e->cxt->fib);
}
- len = MRB_ENV_STACK_LEN(e);
+ len = MRB_ENV_LEN(e);
for (i=0; i<len; i++) {
mrb_gc_mark_value(mrb, e->stack[i]);
}
@@ -698,10 +748,11 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
case MRB_TT_ARRAY:
{
struct RArray *a = (struct RArray*)obj;
- size_t i, e;
+ size_t i, e=ARY_LEN(a);
+ mrb_value *p = ARY_PTR(a);
- for (i=0,e=ARY_LEN(a); i<e; i++) {
- mrb_gc_mark_value(mrb, ARY_PTR(a)[i]);
+ for (i=0; i<e; i++) {
+ mrb_gc_mark_value(mrb, p[i]);
}
}
break;
@@ -712,20 +763,21 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
break;
case MRB_TT_STRING:
- if (RSTR_FSHARED_P(obj) && !RSTR_NOFREE_P(obj)) {
+ if (RSTR_FSHARED_P(obj)) {
struct RString *s = (struct RString*)obj;
mrb_gc_mark(mrb, (struct RBasic*)s->as.heap.aux.fshared);
}
break;
case MRB_TT_RANGE:
- {
- struct RRange *r = (struct RRange*)obj;
+ mrb_gc_mark_range(mrb, (struct RRange*)obj);
+ break;
- if (r->edges) {
- mrb_gc_mark_value(mrb, r->edges->beg);
- mrb_gc_mark_value(mrb, r->edges->end);
- }
+ case MRB_TT_BREAK:
+ {
+ struct RBreak *brk = (struct RBreak*)obj;
+ mrb_gc_mark(mrb, (struct RBasic*)mrb_break_proc_get(brk));
+ mrb_gc_mark_value(mrb, mrb_break_value_get(brk));
}
break;
@@ -739,6 +791,7 @@ mrb_gc_mark(mrb_state *mrb, struct RBasic *obj)
{
if (obj == 0) return;
if (!is_white(obj)) return;
+ if (is_red(obj)) return;
mrb_assert((obj)->tt != MRB_TT_FREE);
add_gray_list(mrb, &mrb->gc, obj);
}
@@ -748,22 +801,6 @@ 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;
@@ -777,18 +814,23 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
case MRB_TT_SCLASS:
mrb_gc_free_mt(mrb, (struct RClass*)obj);
mrb_gc_free_iv(mrb, (struct RObject*)obj);
+ if (!end)
+ mrb_mc_clear_by_class(mrb, (struct RClass*)obj);
break;
case MRB_TT_ICLASS:
if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN))
mrb_gc_free_mt(mrb, (struct RClass*)obj);
+ if (!end)
+ mrb_mc_clear_by_class(mrb, (struct RClass*)obj);
break;
case MRB_TT_ENV:
{
struct REnv *e = (struct REnv*)obj;
- if (MRB_ENV_STACK_SHARED_P(e)) {
+ if (MRB_ENV_ONSTACK_P(e)) {
/* cannot be freed */
- return;
+ e->stack = NULL;
+ break;
}
mrb_free(mrb, e->stack);
e->stack = NULL;
@@ -800,14 +842,14 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
struct mrb_context *c = ((struct RFiber*)obj)->cxt;
if (c && c != mrb->root_c) {
- mrb_callinfo *ci = c->ci;
- mrb_callinfo *ce = c->cibase;
+ if (!end && c->status != MRB_FIBER_TERMINATED) {
+ mrb_callinfo *ci = c->ci;
+ mrb_callinfo *ce = c->cibase;
- if (!end) {
while (ce <= ci) {
- struct REnv *e = ci->env;
- if (e && !is_dead(&mrb->gc, e) &&
- e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
+ struct REnv *e = ci->u.env;
+ if (e && !mrb_object_dead_p(mrb, (struct RBasic*)e) &&
+ e->tt == MRB_TT_ENV && MRB_ENV_ONSTACK_P(e)) {
mrb_env_unshare(mrb, e);
}
ci--;
@@ -839,7 +881,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
struct RProc *p = (struct RProc*)obj;
if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
- mrb_irep *irep = p->body.irep;
+ mrb_irep *irep = (mrb_irep*)p->body.irep;
if (end) {
mrb_irep_cutref(mrb, irep);
}
@@ -849,7 +891,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
break;
case MRB_TT_RANGE:
- mrb_free(mrb, ((struct RRange*)obj)->edges);
+ mrb_gc_free_range(mrb, ((struct RRange*)obj));
break;
case MRB_TT_DATA:
@@ -862,6 +904,24 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
}
break;
+#if defined(MRB_USE_RATIONAL) && defined(MRB_INT64) && defined(MRB_32BIT)
+ case MRB_TT_RATIONAL:
+ {
+ struct RData *o = (struct RData*)obj;
+ mrb_free(mrb, o->iv);
+ }
+ break;
+#endif
+
+#if defined(MRB_USE_COMPLEX) && defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32)
+ case MRB_TT_COMPLEX:
+ {
+ struct RData *o = (struct RData*)obj;
+ mrb_free(mrb, o->iv);
+ }
+ break;
+#endif
+
default:
break;
}
@@ -895,10 +955,10 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc)
mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class);
mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class);
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_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->integer_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);
@@ -925,13 +985,12 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc)
}
}
+/* rough estimation of number of GC marks (non recursive) */
static size_t
-gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
+gc_gray_counts(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++;
@@ -956,7 +1015,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
break;
case MRB_TT_ENV:
- children += MRB_ENV_STACK_LEN(obj);
+ children += MRB_ENV_LEN(obj);
break;
case MRB_TT_FIBER:
@@ -968,7 +1027,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
if (!c || c->status == MRB_FIBER_TERMINATED) break;
/* mark stack */
- i = c->stack - c->stbase;
+ i = c->ci->stack - c->stbase;
if (c->ci) {
i += ci_nregs(c->ci);
@@ -976,9 +1035,6 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
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++)
@@ -1002,6 +1058,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
case MRB_TT_PROC:
case MRB_TT_RANGE:
+ case MRB_TT_BREAK:
children+=2;
break;
@@ -1015,10 +1072,9 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
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;
+ struct RBasic *obj = gc->gray_list;
+ gc->gray_list = obj->gcnext;
+ gc_mark_children(mrb, gc, obj);
}
}
@@ -1029,7 +1085,10 @@ 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);
+ struct RBasic *obj = gc->gray_list;
+ gc->gray_list = obj->gcnext;
+ gc_mark_children(mrb, gc, obj);
+ tried_marks += gc_gray_counts(mrb, gc, obj);
}
return tried_marks;
@@ -1046,7 +1105,9 @@ final_marking_phase(mrb_state *mrb, mrb_gc *gc)
}
mrb_gc_mark_gv(mrb);
mark_context(mrb, mrb->c);
- mark_context(mrb, mrb->root_c);
+ if (mrb->c != mrb->root_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);
@@ -1261,6 +1322,7 @@ mrb_full_gc(mrb_state *mrb)
{
mrb_gc *gc = &mrb->gc;
+ if (!mrb->c) return;
if (gc->disabled || gc->iterating) return;
GC_INVOKE_TIME_REPORT("mrb_full_gc()");
@@ -1284,6 +1346,9 @@ mrb_full_gc(mrb_state *mrb)
gc->full = FALSE;
}
+#ifdef MRB_USE_MALLOC_TRIM
+ malloc_trim(0);
+#endif
GC_TIME_STOP_AND_REPORT;
}
@@ -1568,17 +1633,21 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo
}
}
-#ifdef GC_TEST
-#ifdef GC_DEBUG
-static mrb_value gc_test(mrb_state *, mrb_value);
-#endif
-#endif
+size_t
+mrb_objspace_page_slot_size(void)
+{
+ return sizeof(RVALUE);
+}
+
void
mrb_init_gc(mrb_state *mrb)
{
struct RClass *gc;
+ mrb_static_assert(sizeof(RVALUE) <= sizeof(void*) * 6,
+ "RVALUE size must be within 6 words");
+
gc = mrb_define_module(mrb, "GC");
mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE());
@@ -1590,278 +1659,4 @@ mrb_init_gc(mrb_state *mrb)
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 (p<e) {
- if (is_black(&p->as.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 */
diff --git a/src/hash.c b/src/hash.c
index e1ceec489..918722a2b 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -4,682 +4,1208 @@
** See Copyright Notice in mruby.h
*/
+#include <string.h>
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/hash.h>
#include <mruby/string.h>
#include <mruby/variable.h>
+#include <mruby/presym.h>
-#ifndef MRB_WITHOUT_FLOAT
-/* a function to get hash value of a float number */
-mrb_int mrb_float_id(mrb_float f);
+/*
+ * === Glossary
+ *
+ * [EA]
+ * Entry Array. Store `Hash' entries in insertion order.
+ *
+ * [AR]
+ * Array Table Implementation. The structure of `Hash` that doesn't have a
+ * hash table and linearly searches EA. It is used when `Hash` size <= 16.
+ *
+ * [IB]
+ * Index Buckets. The buckets of hash table, where the bucket value is EA
+ * index. The index is represented by variable length bits according to
+ * the capacity.
+ *
+ * [HT]
+ * Hash Table Implementation. The structure of `Hash` that has IB and is
+ * searched by hash table algorithm. It is used when `Hash` size > 16.
+ * Collision resolution strategy is open addressing method.
+ *
+ * [size]
+ * The number of `Hash` entries (value of `Hash#size`).
+ *
+ * [slot]
+ * The generic term for EA or IB elements.
+ *
+ * [active]
+ * The state in which a slot is recognized as a `Hash` entry.
+ *
+ * [deleted]
+ * The state in which a slot is marked as deleted.
+ *
+ * [used]
+ * The state in which a slot is active or deleted.
+ *
+ * [empty]
+ * The state in which a slot is not used. Capacity is equal to the sum of
+ * the number of used slots and the number of empty slots.
+ */
+
+#define EA_N_RESERVED_INDICES 2 /* empty and deleted */
+#define EA_INCREASE_RATIO 6 / 5 + 6
+#define EA_MAX_INCREASE UINT16_MAX
+#define EA_MAX_CAPA U32(lesser(IB_MAX_CAPA - EA_N_RESERVED_INDICES, MRB_INT_MAX))
+#define IB_MAX_CAPA (U32(1) << IB_MAX_BIT)
+#define IB_TYPE_BIT 32
+#define IB_INIT_BIT ( \
+ ib_upper_bound_for(32) <= AR_MAX_SIZE ? 6 : \
+ ib_upper_bound_for(16) <= AR_MAX_SIZE ? 5 : \
+ 4 \
+)
+#define IB_MAX_BIT (IB_TYPE_BIT - 1)
+#define AR_DEFAULT_CAPA 4
+#define AR_MAX_SIZE 16
+#define H_MAX_SIZE EA_MAX_CAPA
+
+mrb_static_assert1(offsetof(struct RHash, iv) == offsetof(struct RObject, iv));
+mrb_static_assert1(AR_MAX_SIZE < (1 << MRB_HASH_AR_EA_CAPA_BIT));
+
+typedef struct hash_entry {
+ mrb_value key;
+ mrb_value val;
+} hash_entry;
+
+typedef struct hash_table {
+ hash_entry *ea;
+#ifdef MRB_32BIT
+ uint32_t ea_capa;
+ uint32_t ea_n_used;
#endif
+ uint32_t ib[];
+} hash_table;
+
+typedef struct index_buckets_iter {
+ struct RHash *h;
+ uint32_t bit;
+ uint32_t mask;
+ uint32_t pos;
+ uint32_t ary_index;
+ uint32_t ea_index;
+ uint32_t shift1;
+ uint32_t shift2;
+ uint32_t step;
+} index_buckets_iter;
-/* return non zero to break the loop */
-typedef int (sg_foreach_func)(mrb_state *mrb,mrb_value key, mrb_value val, void *data);
+/*
+ * `c_` :: receiver class (category)
+ * `n_` :: attribute name
+ * `t_` :: attribute type
+ * `p_` :: struct member path
+ * `k_` :: macro key
+ */
+#define DEFINE_GETTER(c_, n_, t_, p_) \
+ MRB_INLINE t_ c_##_##n_(const struct RHash *h) {return h->p_;}
+#define DEFINE_SETTER(c_, n_, t_, p_) \
+ MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) {h->p_ = v;}
+#define DEFINE_ACCESSOR(c_, n_, t_, p_) \
+ DEFINE_GETTER(c_, n_, t_, p_) \
+ DEFINE_SETTER(c_, n_, t_, p_)
+#define DEFINE_FLAG_GETTER(c_, n_, t_, k_) \
+ MRB_INLINE t_ c_##_##n_(const struct RHash *h) { \
+ return (t_)((h->flags & MRB_HASH_##k_##_MASK) >> MRB_HASH_##k_##_SHIFT); \
+ }
+#define DEFINE_FLAG_SETTER(c_, n_, t_, k_) \
+ MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) { \
+ h->flags &= ~MRB_HASH_##k_##_MASK; \
+ h->flags |= v << MRB_HASH_##k_##_SHIFT; \
+ }
+#define DEFINE_FLAG_ACCESSOR(c_, n_, t_, k_) \
+ DEFINE_FLAG_GETTER(c_, n_, t_, k_) \
+ DEFINE_FLAG_SETTER(c_, n_, t_, k_)
+#define DEFINE_INCREMENTER(c_, n_) \
+ MRB_INLINE void c_##_inc_##n_(struct RHash *h) { \
+ c_##_set_##n_(h, c_##_##n_(h) + 1); \
+ }
+#define DEFINE_DECREMENTER(c_, n_) \
+ MRB_INLINE void c_##_dec_##n_(struct RHash *h) { \
+ c_##_set_##n_(h, c_##_##n_(h) - 1); \
+ }
+#define DEFINE_SWITCHER(n_, k_) \
+ MRB_INLINE void h_##n_##_on(struct RHash *h) { \
+ h->flags |= MRB_HASH_##k_; \
+ } \
+ MRB_INLINE void h_##n_##_off(struct RHash *h) { \
+ h->flags &= ~MRB_HASH_##k_; \
+ } \
+ MRB_INLINE mrb_bool h_##n_##_p(const struct RHash *h) { \
+ return (h->flags & MRB_HASH_##k_) == MRB_HASH_##k_; \
+ }
-#ifndef MRB_SG_SEGMENT_SIZE
-#define MRB_SG_SEGMENT_SIZE 5
+#ifdef MRB_64BIT
+DEFINE_ACCESSOR(ar, ea_capa, uint32_t, ea_capa)
+DEFINE_ACCESSOR(ar, ea_n_used, uint32_t, ea_n_used)
+DEFINE_ACCESSOR(ht, ea_capa, uint32_t, ea_capa)
+DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, ea_n_used)
+#else
+DEFINE_FLAG_ACCESSOR(ar, ea_capa, uint32_t, AR_EA_CAPA)
+DEFINE_FLAG_ACCESSOR(ar, ea_n_used, uint32_t, AR_EA_N_USED)
+DEFINE_ACCESSOR(ht, ea_capa, uint32_t, hsh.ht->ea_capa)
+DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, hsh.ht->ea_n_used)
#endif
+DEFINE_FLAG_ACCESSOR(ib, bit, uint32_t, IB_BIT)
+DEFINE_ACCESSOR(ar, size, uint32_t, size)
+DEFINE_ACCESSOR(ar, ea, hash_entry*, hsh.ea)
+DEFINE_DECREMENTER(ar, size)
+DEFINE_ACCESSOR(ht, size, uint32_t, size)
+DEFINE_ACCESSOR(ht, ea, hash_entry*, hsh.ht->ea)
+DEFINE_GETTER(ht, ib, uint32_t*, hsh.ht->ib)
+DEFINE_INCREMENTER(ht, size)
+DEFINE_DECREMENTER(ht, size)
+DEFINE_GETTER(h, size, uint32_t, size)
+DEFINE_ACCESSOR(h, ht, hash_table*, hsh.ht)
+DEFINE_SWITCHER(ht, HT)
+
+#define ea_each_used(ea, n_used, entry_var, code) do { \
+ hash_entry *entry_var = ea, *ea_end__ = entry_var + (n_used); \
+ for (; entry_var < ea_end__; ++entry_var) { \
+ code; \
+ } \
+} while (0)
-struct segkv {
- mrb_value key;
- mrb_value val;
-};
-
-typedef struct segment {
- struct segment *next;
- struct segkv e[MRB_SG_SEGMENT_SIZE];
-} segment;
-
-typedef struct segindex {
- size_t size;
- size_t capa;
- struct segkv *table[];
-} segindex;
-
-/* Instance variable table structure */
-typedef struct seglist {
- segment *rootseg;
- segment *lastseg;
- mrb_int size;
- mrb_int last_len;
- segindex *index;
-} seglist;
-
-static /* inline */ size_t
-sg_hash_func(mrb_state *mrb, seglist *t, mrb_value key)
+#define ea_each(ea, size, entry_var, code) do { \
+ hash_entry *entry_var = ea; \
+ uint32_t size__ = size; \
+ for (; 0 < size__; ++entry_var) { \
+ if (entry_deleted_p(entry_var)) continue; \
+ --size__; \
+ code; \
+ } \
+} while (0)
+
+#define ib_cycle_by_key(mrb, h, key, it_var, code) do { \
+ index_buckets_iter it_var[1]; \
+ ib_it_init(mrb, it_var, h, key); \
+ for (;;) { \
+ ib_it_next(it_var); \
+ code; \
+ } \
+} while (0)
+
+#define ib_find_by_key(mrb, h_, key_, it_var, code) do { \
+ mrb_value ib_fbk_key__ = key_; \
+ ib_cycle_by_key(mrb, h_, ib_fbk_key__, it_var, { \
+ if (ib_it_empty_p(it_var)) break; \
+ if (ib_it_deleted_p(it_var)) continue; \
+ if (obj_eql(mrb, ib_fbk_key__, ib_it_entry(it_var)->key, it_var->h)) { \
+ code; \
+ break; \
+ } \
+ }); \
+} while (0)
+
+#define h_each(h, entry_var, code) do { \
+ struct RHash *h__ = h; \
+ hash_entry *h_e_ea__; \
+ uint32_t h_e_size__; \
+ h_ar_p(h) ? (h_e_ea__ = ar_ea(h__), h_e_size__ = ar_size(h__)) : \
+ (h_e_ea__ = ht_ea(h__), h_e_size__ = ht_size(h__)); \
+ ea_each(h_e_ea__, h_e_size__, entry_var, code); \
+} while (0)
+
+/*
+ * In `h_check_modified()`, in the case of `MRB_NO_BOXING`, `ht_ea()` or
+ * `ht_ea_capa()` for AR may read uninitialized area (#5332). Therefore, do
+ * not use those macros for AR in `MRB_NO_BOXING` (but in the case of
+ * `MRB_64BIT`, `ht_ea_capa()` is the same as `ar_ea_capa()`, so use it).
+ */
+#ifdef MRB_NO_BOXING
+# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR FALSE
+# ifdef MRB_64BIT
+# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE
+# else
+# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR FALSE
+# endif /* MRB_64BIT */
+#else
+# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR TRUE
+# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE
+ /*
+ * `h_check_modified` raises an exception when a dangerous modification is
+ * made to `h` by executing `code`.
+ *
+ * `h_check_modified` macro is not called if `h->hsh.ht` (`h->hsh.ea`) is `NULL`
+ * (`Hash` size is zero). And because the `hash_entry` is rather large,
+ * `h->hsh.ht->ea` and `h->hsh.ht->ea_capa` are able to be safely accessed even for
+ * AR. This nature is used to eliminate branch of AR or HT.
+ *
+ * `HT_ASSERT_SAFE_READ` checks if members can be accessed according to its
+ * assumptions.
+ */
+# define HT_ASSERT_SAFE_READ(attr_name) \
+ mrb_static_assert1( \
+ offsetof(hash_table, attr_name) + sizeof(((hash_table*)0)->attr_name) <= \
+ sizeof(hash_entry))
+HT_ASSERT_SAFE_READ(ea);
+# ifdef MRB_32BIT
+HT_ASSERT_SAFE_READ(ea_capa);
+# endif
+# undef HT_ASSERT_SAFE_READ
+#endif /* MRB_NO_BOXING */
+
+/*
+ * `h_check_modified` raises an exception when a dangerous modification is
+ * made to `h` by executing `code`.
+ */
+#define h_check_modified(mrb, h, code) do { \
+ struct RHash *h__ = h; \
+ uint32_t mask__ = MRB_HASH_HT|MRB_HASH_IB_BIT_MASK|MRB_HASH_AR_EA_CAPA_MASK; \
+ uint32_t flags__ = h__->flags & mask__; \
+ void* tbl__ = (mrb_assert(h__->hsh.ht), h__->hsh.ht); \
+ uint32_t ht_ea_capa__ = 0; \
+ hash_entry *ht_ea__ = NULL; \
+ if (H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) { \
+ ht_ea_capa__ = ht_ea_capa(h__); \
+ } \
+ if (H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) { \
+ ht_ea__ = ht_ea(h__); \
+ } \
+ code; \
+ if (flags__ != (h__->flags & mask__) || \
+ tbl__ != h__->hsh.ht || \
+ ((H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) && \
+ ht_ea_capa__ != ht_ea_capa(h__)) || \
+ ((H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) && \
+ ht_ea__ != ht_ea(h__))) { \
+ mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); \
+ } \
+} while (0)
+
+#define U32(v) ((uint32_t)(v))
+#define h_ar_p(h) (!h_ht_p(h))
+#define h_ar_on(h) h_ht_off(h)
+#define lesser(a, b) ((a) < (b) ? (a) : (b))
+#define RHASH_IFNONE(hash) mrb_iv_get(mrb, (hash), MRB_SYM(ifnone))
+#define RHASH_PROCDEFAULT(hash) RHASH_IFNONE(hash)
+
+static uint32_t ib_upper_bound_for(uint32_t capa);
+static uint32_t ib_bit_to_capa(uint32_t bit);
+static void ht_init(
+ mrb_state *mrb, struct RHash *h, uint32_t size,
+ hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit);
+static void ht_set_without_ib_adjustment(
+ mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val);
+
+static uint32_t
+next_power2(uint32_t v)
{
- enum mrb_vtype tt = mrb_type(key);
- mrb_value hv;
- size_t h;
- segindex *index = t->index;
- size_t capa = index ? index->capa : 0;
+ mrb_assert(v != 0);
+#ifdef __GNUC__
+ return U32(1) << ((sizeof(unsigned) * CHAR_BIT) - __builtin_clz(v));
+#else
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+ return v;
+#endif
+}
+static uint32_t
+obj_hash_code(mrb_state *mrb, mrb_value key, struct RHash *h)
+{
+ enum mrb_vtype tt = mrb_type(key);
+ uint32_t hash_code;
+ mrb_value hash_code_obj;
switch (tt) {
case MRB_TT_STRING:
- h = mrb_str_hash(mrb, key);
+ hash_code = 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_INTEGER:
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
#endif
- h = (size_t)mrb_obj_id(key);
+ hash_code = U32(mrb_obj_id(key));
break;
-
default:
- hv = mrb_funcall(mrb, key, "hash", 0);
- h = (size_t)t ^ (size_t)mrb_fixnum(hv);
+ h_check_modified(mrb, h, {
+ hash_code_obj = mrb_funcall_argv(mrb, key, MRB_SYM(hash), 0, NULL);
+ });
+
+ hash_code = U32(tt) ^ U32(mrb_integer(hash_code_obj));
break;
}
- if (index && (index != t->index || capa != index->capa)) {
- mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified");
- }
- return ((h)^((h)<<2)^((h)>>2));
+ return hash_code ^ (hash_code << 2) ^ (hash_code >> 2);
}
-static inline mrb_bool
-sg_hash_equal(mrb_state *mrb, seglist *t, mrb_value a, mrb_value b)
+static mrb_bool
+obj_eql(mrb_state *mrb, mrb_value a, mrb_value b, struct RHash *h)
{
enum mrb_vtype tt = mrb_type(a);
+ mrb_bool eql;
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;
+ if (!mrb_symbol_p(b)) 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;
- }
+ case MRB_TT_INTEGER:
+ if (!mrb_integer_p(b)) return FALSE;
+ return mrb_integer(a) == mrb_integer(b);
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_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;
- }
+ if (!mrb_float_p(b)) return FALSE;
+ return mrb_float(a) == mrb_float(b);
#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;
- }
- }
+ h_check_modified(mrb, h, {eql = mrb_eql(mrb, a, b);});
+ return eql;
+ }
}
-/* Creates the instance variable table. */
-static seglist*
-sg_new(mrb_state *mrb)
+static mrb_bool
+entry_deleted_p(const hash_entry* entry)
{
- seglist *t;
+ return mrb_undef_p(entry->key);
+}
- t = (seglist*)mrb_malloc(mrb, sizeof(seglist));
- t->size = 0;
- t->rootseg = NULL;
- t->lastseg = NULL;
- t->last_len = 0;
- t->index = NULL;
+static void
+entry_delete(hash_entry* entry)
+{
+ entry->key = mrb_undef_value();
+}
- return t;
+static uint32_t
+ea_next_capa_for(uint32_t size, uint32_t max_capa)
+{
+ if (size < AR_DEFAULT_CAPA) {
+ return AR_DEFAULT_CAPA;
+ }
+ else {
+ /*
+ * For 32-bit CPU, the theoretical value of maximum EA capacity is
+ * `UINT32_MAX / sizeof (hash_entry)`. At this time, if
+ * `EA_INCREASE_RATIO` is the current value, 32-bit range will not be
+ * exceeded during the calculation of `capa`, so `size_t` is used.
+ */
+ size_t capa = (size_t)size * EA_INCREASE_RATIO, inc = capa - size;
+ if (EA_MAX_INCREASE < inc) capa = size + EA_MAX_INCREASE;
+ return capa <= max_capa ? U32(capa) : max_capa;
+ }
}
-#define power2(v) do { \
- v--;\
- v |= v >> 1;\
- v |= v >> 2;\
- v |= v >> 4;\
- v |= v >> 8;\
- v |= v >> 16;\
- v++;\
-} while (0)
+static hash_entry*
+ea_resize(mrb_state *mrb, hash_entry *ea, uint32_t capa)
+{
+ return (hash_entry*)mrb_realloc(mrb, ea, sizeof(hash_entry) * capa);
+}
-#ifndef UPPER_BOUND
-#define UPPER_BOUND(x) ((x)>>2|(x)>>1)
-#endif
+static void
+ea_compress(hash_entry *ea, uint32_t n_used)
+{
+ hash_entry *w_entry = ea;
+ ea_each_used(ea, n_used, r_entry, {
+ if (entry_deleted_p(r_entry)) continue;
+ if (r_entry != w_entry) *w_entry = *r_entry;
+ ++w_entry;
+ });
+}
+
+/*
+ * Increase or decrease capacity of `ea` to a standard size that can
+ * accommodate `*capap + 1` entries (but, not exceed `max_capa`). Set the
+ * changed capacity to `*capap` and return a pointer to `mrb_realloc`ed EA.
+ */
+static hash_entry*
+ea_adjust(mrb_state *mrb, hash_entry *ea, uint32_t *capap, uint32_t max_capa)
+{
+ *capap = ea_next_capa_for(*capap, max_capa);
+ return ea_resize(mrb, ea, *capap);
+}
-#define SG_MASK(index) ((index->capa)-1)
+static hash_entry*
+ea_dup(mrb_state *mrb, const hash_entry *ea, uint32_t capa)
+{
+ size_t byte_size = sizeof(hash_entry) * capa;
+ hash_entry *new_ea = (hash_entry*)mrb_malloc(mrb, byte_size);
+ return (hash_entry*)memcpy(new_ea, ea, byte_size);
+}
+
+static hash_entry*
+ea_get_by_key(mrb_state *mrb, hash_entry *ea, uint32_t size, mrb_value key,
+ struct RHash *h)
+{
+ ea_each(ea, size, entry, {
+ if (obj_eql(mrb, key, entry->key, h)) return entry;
+ });
+ return NULL;
+}
+
+static hash_entry*
+ea_get(hash_entry *ea, uint32_t index)
+{
+ return &ea[index];
+}
-/* Build index for the segment list */
static void
-sg_index(mrb_state *mrb, seglist *t)
-{
- size_t size = (size_t)t->size;
- size_t mask;
- segindex *index = t->index;
- segment *seg;
- size_t i;
-
- if (size < MRB_SG_SEGMENT_SIZE) {
- if (index) {
- failed:
- mrb_free(mrb, index);
- t->index = NULL;
- }
- return;
- }
- /* 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) goto failed;
- t->index = index;
- }
- index->size = t->size;
- index->capa = size;
- for (i=0; i<size; i++) {
- index->table[i] = NULL;
- }
+ea_set(hash_entry *ea, uint32_t index, mrb_value key, mrb_value val)
+{
+ ea[index].key = key;
+ ea[index].val = val;
+}
- /* rebuld index */
- mask = SG_MASK(index);
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) {
- mrb_value key;
- size_t k, step = 0;
+static void
+ar_init(struct RHash *h, uint32_t size,
+ hash_entry *ea, uint32_t ea_capa, uint32_t ea_n_used)
+{
+ h_ar_on(h);
+ ar_set_size(h, size);
+ ar_set_ea(h, ea);
+ ar_set_ea_capa(h, ea_capa);
+ ar_set_ea_n_used(h, ea_n_used);
+}
- if (!seg->next && i >= (size_t)t->last_len) {
- return;
- }
- key = seg->e[i].key;
- if (mrb_undef_p(key)) continue;
- k = sg_hash_func(mrb, t, key) & mask;
- while (index->table[k]) {
- k = (k+(++step)) & mask;
- }
- index->table[k] = &seg->e[i];
- }
- seg = seg->next;
- }
+static void
+ar_free(mrb_state *mrb, struct RHash *h)
+{
+ mrb_free(mrb, ar_ea(h));
}
-/* Compacts the segment list removing deleted entries. */
static void
-sg_compact(mrb_state *mrb, seglist *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) {
- sg_index(mrb, t);
- return;
- }
- while (seg) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) {
- mrb_value k = seg->e[i].key;
+ar_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa)
+{
+ uint32_t ea_capa = size;
+ hash_entry *ea = ea_adjust(mrb, ar_ea(h), &ea_capa, max_ea_capa);
+ ar_set_ea(h, ea);
+ ar_set_ea_capa(h, ea_capa);
+}
- if (!seg->next && i >= t->last_len) {
- goto exit;
- }
- if (mrb_undef_p(k)) { /* found delete key */
- if (seg2 == NULL) {
- seg2 = seg;
- i2 = i;
+static void
+ar_compress(mrb_state *mrb, struct RHash *h)
+{
+ uint32_t size = ar_size(h);
+ ea_compress(ar_ea(h), ar_ea_n_used(h));
+ ar_set_ea_n_used(h, size);
+ ar_adjust_ea(mrb, h, size, lesser(ar_ea_capa(h), AR_MAX_SIZE));
+}
+
+static mrb_bool
+ar_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ ea_each(ar_ea(h), ar_size(h), entry, {
+ if (!obj_eql(mrb, key, entry->key, h)) continue;
+ *valp = entry->val;
+ return TRUE;
+ });
+ return FALSE;
+}
+
+static void
+ar_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
+{
+ uint32_t size = ar_size(h);
+ hash_entry *entry;
+ if ((entry = ea_get_by_key(mrb, ar_ea(h), size, key, h))) {
+ entry->val = val;
+ }
+ else {
+ uint32_t ea_capa = ar_ea_capa(h), ea_n_used = ar_ea_n_used(h);
+ if (ea_capa == ea_n_used) {
+ if (size == ea_n_used) {
+ if (size == AR_MAX_SIZE) {
+ hash_entry *ea = ea_adjust(mrb, ar_ea(h), &ea_capa, EA_MAX_CAPA);
+ ea_set(ea, ea_n_used, key, val);
+ ht_init(mrb, h, ++size, ea, ea_capa, NULL, IB_INIT_BIT);
+ return;
+ }
+ else {
+ ar_adjust_ea(mrb, h, size, AR_MAX_SIZE);
}
}
else {
- size++;
- if (seg2 != NULL) {
- seg2->e[i2++] = seg->e[i];
- if (i2 >= MRB_SG_SEGMENT_SIZE) {
- seg2 = seg2->next;
- i2 = 0;
- }
- }
+ ar_compress(mrb, h);
+ ea_n_used = size;
}
}
- 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) {
- sg_index(mrb, t);
+ ea_set(ar_ea(h), ea_n_used, key, val);
+ ar_set_size(h, ++size);
+ ar_set_ea_n_used(h, ++ea_n_used);
}
}
-/* Set the value for the key in the indexed segment list. */
+static mrb_bool
+ar_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ hash_entry *entry = ea_get_by_key(mrb, ar_ea(h), ar_size(h), key, h);
+ if (!entry) return FALSE;
+ *valp = entry->val;
+ entry_delete(entry);
+ ar_dec_size(h);
+ return TRUE;
+}
+
static void
-sg_index_put(mrb_state *mrb, seglist *t, mrb_value key, mrb_value val)
+ar_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp)
{
- segindex *index = t->index;
- size_t k, sp, step = 0, mask;
- segment *seg;
+ uint32_t size = ar_size(h);
+ ea_each(ar_ea(h), size, entry, {
+ *keyp = entry->key;
+ *valp = entry->val;
+ entry_delete(entry);
+ ar_set_size(h, --size);
+ return;
+ });
+}
- if (index->size >= UPPER_BOUND(index->capa)) {
- /* need to expand table */
- sg_compact(mrb, t);
- index = t->index;
- }
- mask = SG_MASK(index);
- sp = index->capa;
- k = sg_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;
+static void
+ar_rehash(mrb_state *mrb, struct RHash *h)
+{
+ /* see comments in `h_rehash` */
+ uint32_t size = ar_size(h), w_size = 0, ea_capa = ar_ea_capa(h);
+ hash_entry *ea = ar_ea(h), *w_entry;
+ ea_each(ea, size, r_entry, {
+ if ((w_entry = ea_get_by_key(mrb, ea, w_size, r_entry->key, h))) {
+ w_entry->val = r_entry->val;
+ ar_set_size(h, --size);
+ entry_delete(r_entry);
}
- else if (sg_hash_equal(mrb, t, key, key2)) {
- index->table[k]->val = val;
- return;
+ else {
+ if (w_size != U32(r_entry - ea)) {
+ ea_set(ea, w_size, r_entry->key, r_entry->val);
+ entry_delete(r_entry);
+ }
+ ++w_size;
}
- k = (k+(++step)) & mask;
- }
- if (sp < index->capa) {
- k = sp;
- }
+ });
+ mrb_assert(size == w_size);
+ ar_set_ea_n_used(h, size);
+ ar_adjust_ea(mrb, h, size, ea_capa);
+}
- /* put the value at the last */
- seg = t->lastseg;
- if (t->last_len < MRB_SG_SEGMENT_SIZE) {
- index->table[k] = &seg->e[t->last_len++];
- }
- else { /* append a new segment */
- seg->next = (segment*)mrb_malloc(mrb, sizeof(segment));
- 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++;
+static uint32_t
+ib_it_pos_for(index_buckets_iter *it, uint32_t v)
+{
+ return v & it->mask;
}
-/* Set the value for the key in the segment list. */
-static void
-sg_put(mrb_state *mrb, seglist *t, mrb_value key, mrb_value val)
+static uint32_t
+ib_it_empty_value(const index_buckets_iter *it)
{
- segment *seg;
- mrb_int i, deleted = 0;
+ return it->mask;
+}
- if (t == NULL) return;
- if (t->index) {
- sg_index_put(mrb, t, key, val);
- return;
- }
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; 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 (sg_hash_equal(mrb, t, k, key)) {
- seg->e[i].val = val;
- return;
- }
- }
- seg = seg->next;
- }
+static uint32_t
+ib_it_deleted_value(const index_buckets_iter *it)
+{
+ return it->mask - 1;
+}
- /* Not found */
- if (deleted > MRB_SG_SEGMENT_SIZE) {
- sg_compact(mrb, t);
- }
- t->size++;
-
- seg = (segment*)mrb_malloc(mrb, sizeof(segment));
- seg->next = NULL;
- seg->e[0].key = key;
- seg->e[0].val = val;
- t->last_len = 1;
- if (t->rootseg == NULL) {
- t->rootseg = seg;
- }
- else {
- t->lastseg->next = seg;
- }
- t->lastseg = seg;
- if (t->index == NULL && t->size > MRB_SG_SEGMENT_SIZE*4) {
- sg_index(mrb, t);
- }
+static mrb_bool
+ib_it_empty_p(const index_buckets_iter *it)
+{
+ return it->ea_index == ib_it_empty_value(it);
}
-/* Get a value for a key from the indexed segment list. */
static mrb_bool
-sg_index_get(mrb_state *mrb, seglist *t, mrb_value key, mrb_value *vp)
-{
- segindex *index = t->index;
- size_t mask = SG_MASK(index);
- size_t k = sg_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) && sg_hash_equal(mrb, t, key, key2)) {
- if (vp) *vp = index->table[k]->val;
- return TRUE;
- }
- k = (k+(++step)) & mask;
- }
- return FALSE;
+ib_it_deleted_p(const index_buckets_iter *it)
+{
+ return it->ea_index == ib_it_deleted_value(it);
}
-/* Get a value for a key from the segment list. */
static mrb_bool
-sg_get(mrb_state *mrb, seglist *t, mrb_value key, mrb_value *vp)
+ib_it_active_p(const index_buckets_iter *it)
+{
+ return it->ea_index < ib_it_deleted_value(it);
+}
+
+static void
+ib_it_init(mrb_state *mrb, index_buckets_iter *it, struct RHash *h, mrb_value key)
{
- segment *seg;
- mrb_int i;
+ it->h = h;
+ it->bit = ib_bit(h);
+ it->mask = ib_bit_to_capa(it->bit) - 1;
+ it->pos = ib_it_pos_for(it, obj_hash_code(mrb, key, h));
+ it->step = 0;
+}
- if (t == NULL) return FALSE;
- if (t->index) {
- return sg_index_get(mrb, t, key, vp);
+static void
+ib_it_next(index_buckets_iter *it)
+{
+ /*
+ * [IB image]
+ *
+ * ary_index(1) --.
+ * \ .-- shift1(3) .-- shift2(29)
+ * pos(6) --. \ / /
+ * View | \ \ <-o-> <----------o---------->
+ * -------- +---------------------\----\--+-----------------------------+-----
+ * array | 0 `--. `-|--- o 1 | ...
+ * +---------+---------+-----+\--+-----+---------+---------+---+-----
+ * buckets | 0 | 1 | ... | o 6 | 7 | 8 | ...
+ * +---------+---------+-----+=========+---------+---------+---------
+ * bit set |1 1 1 0 0|0 0 0 1 1| ... |0 1 0 1 1|0 1 1 1 0|0 1 0 1 0| ...
+ * +---------+---------+-----+========*+---------+---------+---------
+ * <---o---> \
+ * \ `-- bit_pos(34)
+ * `-- bit(5)
+ */
+
+ /* Slide to handle as `capa == 32` to avoid 64-bit operations */
+ uint32_t slid_pos = it->pos & (IB_TYPE_BIT - 1);
+ uint32_t slid_bit_pos = it->bit * (slid_pos + 1) - 1;
+ uint32_t slid_ary_index = slid_bit_pos / IB_TYPE_BIT;
+ it->ary_index = slid_ary_index + it->pos / IB_TYPE_BIT * it->bit;
+ it->shift2 = (slid_ary_index + 1) * IB_TYPE_BIT - slid_bit_pos - 1;
+ it->ea_index = (ht_ib(it->h)[it->ary_index] >> it->shift2) & it->mask;
+ if (IB_TYPE_BIT - it->bit < it->shift2) {
+ it->shift1 = IB_TYPE_BIT - it->shift2;
+ it->ea_index |= (ht_ib(it->h)[it->ary_index - 1] << it->shift1) & it->mask;
+ }
+ else {
+ it->shift1 = 0;
}
+ it->pos = ib_it_pos_for(it, it->pos + (++it->step));
+}
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) {
- mrb_value k = seg->e[i].key;
+static uint32_t
+ib_it_get(const index_buckets_iter *it)
+{
+ return it->ea_index;
+}
- if (!seg->next && i >= t->last_len) {
- return FALSE;
- }
- if (mrb_undef_p(k)) continue;
- if (sg_hash_equal(mrb, t, k, key)) {
- if (vp) *vp = seg->e[i].val;
- return TRUE;
- }
- }
- seg = seg->next;
+static void
+ib_it_set(index_buckets_iter *it, uint32_t ea_index)
+{
+ uint32_t mask, i;
+ it->ea_index = ea_index;
+ if (it->shift1) {
+ i = it->ary_index - 1;
+ mask = it->mask >> it->shift1;
+ ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index >> it->shift1);
}
- return FALSE;
+ i = it->ary_index;
+ mask = it->mask << it->shift2;
+ ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index << it->shift2);
+}
+
+static void
+ib_it_delete(index_buckets_iter *it)
+{
+ ib_it_set(it, ib_it_deleted_value(it));
+}
+
+static hash_entry*
+ib_it_entry(index_buckets_iter *it)
+{
+ return ea_get(ht_ea(it->h), it->ea_index);
+}
+
+static uint32_t
+ib_capa_to_bit(uint32_t capa)
+{
+#ifdef __GNUC__
+ return U32(__builtin_ctz(capa));
+#else
+ /* http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn */
+ static const uint32_t MultiplyDeBruijnBitPosition2[] = {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+ };
+ return MultiplyDeBruijnBitPosition2[U32(capa * 0x077CB531U) >> 27];
+#endif
+}
+
+static uint32_t
+ib_bit_to_capa(uint32_t bit)
+{
+ return U32(1) << bit;
+}
+
+static uint32_t
+ib_upper_bound_for(uint32_t capa)
+{
+ return (capa >> 2) | (capa >> 1); /* 3/4 */
+}
+
+static uint32_t
+ib_bit_for(uint32_t size)
+{
+ uint32_t capa = next_power2(size);
+ if (capa != IB_MAX_CAPA && ib_upper_bound_for(capa) < size) capa *= 2;
+ return ib_capa_to_bit(capa);
+}
+
+static uint32_t
+ib_byte_size_for(uint32_t ib_bit)
+{
+ mrb_assert(IB_INIT_BIT <= ib_bit);
+ uint32_t ary_size = IB_INIT_BIT == 4 ?
+ ib_bit_to_capa(ib_bit) * 2 / IB_TYPE_BIT * ib_bit / 2 :
+ ib_bit_to_capa(ib_bit) / IB_TYPE_BIT * ib_bit;
+ return U32(sizeof(uint32_t) * ary_size);
+}
+
+static void
+ib_init(mrb_state *mrb, struct RHash *h, uint32_t ib_bit, size_t ib_byte_size)
+{
+ hash_entry *ea = ht_ea(h);
+ memset(ht_ib(h), 0xff, ib_byte_size);
+ ib_set_bit(h, ib_bit);
+ ea_each_used(ea, ht_ea_n_used(h), entry, {
+ ib_cycle_by_key(mrb, h, entry->key, it, {
+ if (!ib_it_empty_p(it)) continue;
+ ib_it_set(it, U32(entry - ea));
+ break;
+ });
+ });
+}
+
+static void
+ht_init(mrb_state *mrb, struct RHash *h, uint32_t size,
+ hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit)
+{
+ size_t ib_byte_size = ib_byte_size_for(ib_bit);
+ size_t ht_byte_size = sizeof(hash_table) + ib_byte_size;
+ h_ht_on(h);
+ h_set_ht(h, (hash_table*)mrb_realloc(mrb, ht, ht_byte_size));
+ ht_set_size(h, size);
+ ht_set_ea(h, ea);
+ ht_set_ea_capa(h, ea_capa);
+ ht_set_ea_n_used(h, size);
+ ib_init(mrb, h, ib_bit, ib_byte_size);
+}
+
+static void
+ht_free(mrb_state *mrb, struct RHash *h)
+{
+ mrb_free(mrb, ht_ea(h));
+ mrb_free(mrb, h_ht(h));
+}
+
+static hash_table*
+ht_dup(mrb_state *mrb, const struct RHash *h)
+{
+ size_t ib_byte_size = ib_byte_size_for(ib_bit(h));
+ size_t ht_byte_size = sizeof(hash_table) + ib_byte_size;
+ hash_table *new_ht = (hash_table*)mrb_malloc(mrb, ht_byte_size);
+ return (hash_table*)memcpy(new_ht, h_ht(h), ht_byte_size);
+}
+
+static void
+ht_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa)
+{
+ uint32_t ea_capa = size;
+ hash_entry *ea = ea_adjust(mrb, ht_ea(h), &ea_capa, max_ea_capa);
+ ht_set_ea(h, ea);
+ ht_set_ea_capa(h, ea_capa);
+}
+
+static void
+ht_to_ar(mrb_state *mrb, struct RHash *h)
+{
+ uint32_t size = ht_size(h), ea_capa = size;
+ hash_entry *ea = ht_ea(h);
+ ea_compress(ea, ht_ea_n_used(h));
+ ea = ea_adjust(mrb, ea, &ea_capa, AR_MAX_SIZE);
+ mrb_free(mrb, h_ht(h));
+ ar_init(h, size, ea, ea_capa, size);
}
-/* Deletes the value for the symbol from the instance variable table. */
-/* Deletion is done by overwriting keys by `undef`. */
static mrb_bool
-sg_del(mrb_state *mrb, seglist *t, mrb_value key, mrb_value *vp)
+ht_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
{
- segment *seg;
- mrb_int i;
+ ib_find_by_key(mrb, h, key, it, {
+ *valp = ib_it_entry(it)->val;
+ return TRUE;
+ });
+ return FALSE;
+}
- if (t == NULL) return FALSE;
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) {
- mrb_value key2;
+static void
+ht_set_as_ar(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
+{
+ ht_to_ar(mrb, h);
+ ar_set(mrb, h, key, val);
+}
- if (!seg->next && i >= t->last_len) {
- /* not found */
- return FALSE;
+static void
+ht_set_without_ib_adjustment(mrb_state *mrb, struct RHash *h,
+ mrb_value key, mrb_value val)
+{
+ mrb_assert(ht_size(h) < ib_bit_to_capa(ib_bit(h)));
+ ib_cycle_by_key(mrb, h, key, it, {
+ if (ib_it_active_p(it)) {
+ if (!obj_eql(mrb, key, ib_it_entry(it)->key, h)) continue;
+ ib_it_entry(it)->val = val;
+ }
+ else {
+ uint32_t ea_n_used = ht_ea_n_used(h);
+ if (ea_n_used == H_MAX_SIZE) {
+ mrb_assert(ht_size(h) == ea_n_used);
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big");
}
- key2 = seg->e[i].key;
- if (!mrb_undef_p(key2) && sg_hash_equal(mrb, t, key, key2)) {
- if (vp) *vp = seg->e[i].val;
- seg->e[i].key = mrb_undef_value();
- t->size--;
- return TRUE;
+ if (ea_n_used == ht_ea_capa(h)) ht_adjust_ea(mrb, h, ea_n_used, EA_MAX_CAPA);
+ ib_it_set(it, ea_n_used);
+ ea_set(ht_ea(h), ea_n_used, key, val);
+ ht_inc_size(h);
+ ht_set_ea_n_used(h, ++ea_n_used);
+ }
+ return;
+ });
+}
+
+static void
+ht_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
+{
+ uint32_t size = ht_size(h);
+ uint32_t ib_bit_width = ib_bit(h), ib_capa = ib_bit_to_capa(ib_bit_width);
+ if (ib_upper_bound_for(ib_capa) <= size) {
+ if (size != ht_ea_n_used(h)) ea_compress(ht_ea(h), ht_ea_n_used(h));
+ ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ++ib_bit_width);
+ }
+ else if (size != ht_ea_n_used(h)) {
+ if (ib_capa - EA_N_RESERVED_INDICES <= ht_ea_n_used(h)) goto compress;
+ if (ht_ea_capa(h) == ht_ea_n_used(h)) {
+ if (size <= AR_MAX_SIZE) {ht_set_as_ar(mrb, h, key, val); return;}
+ if (ea_next_capa_for(size, EA_MAX_CAPA) <= ht_ea_capa(h)) {
+ compress:
+ ea_compress(ht_ea(h), ht_ea_n_used(h));
+ ht_adjust_ea(mrb, h, size, ht_ea_capa(h));
+ ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ib_bit_width);
}
}
- seg = seg->next;
}
+ ht_set_without_ib_adjustment(mrb, h, key, val);
+}
+
+static mrb_bool
+ht_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ ib_find_by_key(mrb, h, key, it, {
+ hash_entry *entry = ib_it_entry(it);
+ *valp = entry->val;
+ ib_it_delete(it);
+ entry_delete(entry);
+ ht_dec_size(h);
+ return TRUE;
+ });
return FALSE;
}
-/* Iterates over the instance variable table. */
static void
-sg_foreach(mrb_state *mrb, seglist *t, sg_foreach_func *func, void *p)
+ht_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp)
{
- segment *seg;
- mrb_int i;
+ hash_entry *ea = ht_ea(h);
+ ea_each(ea, ht_size(h), entry, {
+ ib_cycle_by_key(mrb, h, entry->key, it, {
+ if (ib_it_get(it) != U32(entry - ea)) continue;
+ *keyp = entry->key;
+ *valp = entry->val;
+ ib_it_delete(it);
+ entry_delete(entry);
+ ht_dec_size(h);
+ return;
+ });
+ });
+}
- if (t == NULL) return;
- if (t->index && t->index->size-(size_t)t->size > MRB_SG_SEGMENT_SIZE) {
- sg_compact(mrb, t);
+static void
+ht_rehash(mrb_state *mrb, struct RHash *h)
+{
+ /* see comments in `h_rehash` */
+ uint32_t size = ht_size(h);
+ if (size <= AR_MAX_SIZE) {
+ ht_to_ar(mrb, h);
+ ar_rehash(mrb, h);
+ return;
}
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) {
- /* no value in last segment after last_len */
- if (!seg->next && i >= t->last_len) {
- return;
+ uint32_t w_size = 0, ea_capa = ht_ea_capa(h);
+ hash_entry *ea = ht_ea(h);
+ ht_init(mrb, h, 0, ea, ea_capa, h_ht(h), ib_bit_for(size));
+ ht_set_size(h, size);
+ ht_set_ea_n_used(h, ht_ea_n_used(h));
+ ea_each(ea, size, r_entry, {
+ ib_cycle_by_key(mrb, h, r_entry->key, it, {
+ if (ib_it_active_p(it)) {
+ if (!obj_eql(mrb, r_entry->key, ib_it_entry(it)->key, h)) continue;
+ ib_it_entry(it)->val = r_entry->val;
+ ht_set_size(h, --size);
+ entry_delete(r_entry);
}
- 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;
+ else {
+ if (w_size != U32(r_entry - ea)) {
+ ea_set(ea, w_size, r_entry->key, r_entry->val);
+ entry_delete(r_entry);
+ }
+ ib_it_set(it, w_size++);
+ }
+ break;
+ });
+ });
+ mrb_assert(size == w_size);
+ ht_set_ea_n_used(h, size);
+ size <= AR_MAX_SIZE ? ht_to_ar(mrb, h) : ht_adjust_ea(mrb, h, size, ea_capa);
+}
+
+static mrb_value
+h_key_for(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;
}
-/* Get the size of the instance variable table. */
-static mrb_int
-sg_size(mrb_state *mrb, seglist *t)
+static struct RHash*
+h_alloc(mrb_state *mrb)
{
- if (t == NULL) return 0;
- return t->size;
+ return MRB_OBJ_ALLOC(mrb, MRB_TT_HASH, mrb->hash_class);
}
-/* Copy the instance variable table. */
-static seglist*
-sg_copy(mrb_state *mrb, seglist *t)
+static void
+h_init(struct RHash *h)
{
- segment *seg;
- seglist *t2;
- mrb_int i;
+ ar_init(h, 0, NULL, 0, 0);
+}
- seg = t->rootseg;
- t2 = sg_new(mrb);
+static void
+h_free_table(mrb_state *mrb, struct RHash *h)
+{
+ (h_ar_p(h) ? ar_free : ht_free)(mrb, h);
+}
- while (seg != NULL) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) {
- mrb_value key = seg->e[i].key;
- mrb_value val = seg->e[i].val;
+static void
+h_clear(mrb_state *mrb, struct RHash *h)
+{
+ h_free_table(mrb, h);
+ h_init(h);
+}
- if ((seg->next == NULL) && (i >= t->last_len)) {
- return t2;
- }
- sg_put(mrb, t2, key, val);
- }
- seg = seg->next;
- }
- return t2;
+static mrb_bool
+h_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ return (h_ar_p(h) ? ar_get : ht_get)(mrb, h, key, valp);
}
-/* Free memory of the instance variable table. */
static void
-sg_free(mrb_state *mrb, seglist *t)
+h_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val)
{
- segment *seg;
+ (h_ar_p(h) ? ar_set : ht_set)(mrb, h, key, val);
+}
- 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 mrb_bool
+h_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp)
+{
+ return (h_ar_p(h) ? ar_delete : ht_delete)(mrb, h, key, valp);
}
-static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
+/* find first element in the table, and remove it. */
+static void
+h_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp)
+{
+ (h_ar_p(h) ? ar_shift : ht_shift)(mrb, h, keyp, valp);
+}
-static inline mrb_value
-ht_key(mrb_state *mrb, mrb_value key)
+static void
+h_rehash(mrb_state *mrb, struct RHash *h)
{
- 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));
+ /*
+ * ==== Comments common to `ar_rehash` and `ht_rehash`
+ *
+ * - Because reindex (such as elimination of duplicate keys) must be
+ * guaranteed, it is necessary to set one by one.
+ *
+ * - To prevent EA from breaking if an exception occurs in the middle,
+ * delete the slot before moving when moving the entry, and update size
+ * at any time when overwriting.
+ */
+ (h_size(h) == 0 ? h_clear : h_ar_p(h) ? ar_rehash : ht_rehash)(mrb, h);
+}
+
+static void
+h_replace(mrb_state *mrb, struct RHash *h, struct RHash *orig_h)
+{
+ uint32_t size = h_size(orig_h);
+ if (size == 0) {
+ h_clear(mrb, h);
+ }
+ else if (h_ar_p(orig_h)) {
+ uint32_t ea_capa = ar_ea_capa(orig_h);
+ hash_entry *ea = ea_dup(mrb, ar_ea(orig_h), ea_capa);
+ h_free_table(mrb, h);
+ ar_init(h, size, ea, ea_capa, ar_ea_n_used(orig_h));
+ }
+ else { /* HT */
+ uint32_t ea_capa = ht_ea_capa(orig_h);
+ hash_entry *ea = ea_dup(mrb, ht_ea(orig_h), ea_capa);
+ hash_table *ht = ht_dup(mrb, orig_h);
+ h_free_table(mrb, h);
+ h_ht_on(h);
+ h_set_ht(h, ht);
+ ht_set_size(h, size);
+ ht_set_ea(h, ea);
+#ifdef MRB_64BIT
+ ht_set_ea_capa(h, ea_capa);
+ ht_set_ea_n_used(h, ht_ea_n_used(orig_h));
+#endif
+ ib_set_bit(h, ib_bit(orig_h));
}
- return key;
}
-#define KEY(key) ht_key(mrb, key)
+void
+mrb_gc_mark_hash(mrb_state *mrb, struct RHash *h)
+{
+ h_each(h, entry, {
+ mrb_gc_mark_value(mrb, entry->key);
+ mrb_gc_mark_value(mrb, entry->val);
+ });
+}
-static int
-hash_mark_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
+size_t
+mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *h)
{
- mrb_gc_mark_value(mrb, key);
- mrb_gc_mark_value(mrb, val);
- return 0;
+ return h_size(h) * 2;
}
void
-mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash)
+mrb_gc_free_hash(mrb_state *mrb, struct RHash *h)
{
- sg_foreach(mrb, hash->ht, hash_mark_i, NULL);
+ h_free_table(mrb, h);
}
size_t
-mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash)
+mrb_hash_memsize(mrb_value self)
{
- if (!hash->ht) return 0;
- return sg_size(mrb, hash->ht)*2;
+ struct RHash *h = mrb_hash_ptr(self);
+ return mrb_obj_iv_tbl_memsize(self) +
+ (h_ar_p(h) ? (ar_ea_capa(h) * sizeof(hash_entry)) :
+ (ht_ea_capa(h) * sizeof(hash_entry) +
+ sizeof(hash_table) +
+ ib_byte_size_for(ib_bit(h))));
}
-void
-mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash)
+/* Iterates over the key/value pairs. */
+MRB_API void
+mrb_hash_foreach(mrb_state *mrb, struct RHash *h, mrb_hash_foreach_func *func, void *data)
{
- sg_free(mrb, hash->ht);
+ h_each(h, entry, {
+ if (func(mrb, entry->key, entry->val, data) != 0) return;
+ });
}
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;
+ struct RHash *h = h_alloc(mrb);
return mrb_obj_value(h);
}
+/*
+ * Set the capacity of EA and IB to minimum capacity (and appropriate load
+ * factor) that does not cause expansion when inserting `capa` elements.
+ */
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 segment list */
- h->ht = sg_new(mrb);
- /* capacity ignored */
- h->iv = 0;
- return mrb_obj_value(h);
+ if (capa < 0 || EA_MAX_CAPA < capa) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big");
+ return mrb_nil_value(); /* not reached */
+ }
+ else if (capa == 0) {
+ return mrb_hash_new(mrb);
+ }
+ else {
+ uint32_t size = U32(capa);
+ struct RHash *h = h_alloc(mrb);
+ hash_entry *ea = ea_resize(mrb, NULL, size);
+ if (size <= AR_MAX_SIZE) {
+ ar_init(h, 0, ea, size, 0);
+ }
+ else {
+ ht_init(mrb, h, 0, ea, size, NULL, ib_bit_for(size));
+ }
+ 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 void
+hash_modify(mrb_state *mrb, mrb_value hash)
+{
+ mrb_check_frozen(mrb, mrb_hash_ptr(hash));
+}
static mrb_value
-mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
+hash_default(mrb_state *mrb, mrb_value hash, mrb_value key)
{
- mrb_value orig;
- struct RHash* copy;
- seglist *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");
+ if (MRB_RHASH_DEFAULT_P(hash)) {
+ if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+ return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key);
+ }
+ else {
+ return RHASH_IFNONE(hash);
+ }
}
+ return mrb_nil_value();
+}
- orig_h = RHASH_TBL(self);
- copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
- copy->ht = sg_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;
+static void
+hash_replace(mrb_state *mrb, mrb_value self, mrb_value orig)
+{
+ struct RHash *h = mrb_hash_ptr(self), *orig_h = mrb_hash_ptr(orig);
+ uint32_t mask = MRB_HASH_DEFAULT | MRB_HASH_PROC_DEFAULT;
+ mrb_sym name;
+ h_replace(mrb, h, orig_h);
+ name = MRB_SYM(ifnone);
+ if (orig_h->flags & MRB_HASH_DEFAULT) {
+ mrb_iv_set(mrb, self, name, mrb_iv_get(mrb, orig, name));
}
- 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);
+ else {
+ mrb_iv_remove(mrb, self, name);
}
- return vret;
+ h->flags &= ~mask;
+ h->flags |= orig_h->flags & mask;
}
-static int
-check_kdict_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data)
+static mrb_value
+mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
{
- if (!mrb_symbol_p(key)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys");
- }
- return 0;
+ mrb_value orig;
+ mrb_get_args(mrb, "H", &orig);
+ hash_modify(mrb, self);
+ if (mrb_hash_ptr(self) != mrb_hash_ptr(orig)) hash_replace(mrb, self, orig);
+ return self;
}
void
mrb_hash_check_kdict(mrb_state *mrb, mrb_value self)
{
- seglist *sg;
-
- sg = RHASH_TBL(self);
- if (!sg || sg_size(mrb, sg) == 0) return;
- sg_foreach(mrb, sg, check_kdict_i, NULL);
+ h_each(mrb_hash_ptr(self), entry, {
+ if (mrb_symbol_p(entry->key)) continue;
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys");
+ });
}
MRB_API mrb_value
mrb_hash_dup(mrb_state *mrb, mrb_value self)
{
- struct RHash* copy;
- seglist *orig_h;
-
- orig_h = RHASH_TBL(self);
- copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
- copy->ht = orig_h ? sg_copy(mrb, orig_h) : NULL;
- return mrb_obj_value(copy);
+ struct RHash* copy_h = h_alloc(mrb);
+ mrb_value copy = mrb_obj_value(copy_h);
+ copy_h->c = mrb_hash_ptr(self)->c;
+ hash_replace(mrb, copy, self);
+ return copy;
}
MRB_API mrb_value
@@ -688,11 +1214,11 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
mrb_value val;
mrb_sym mid;
- if (sg_get(mrb, RHASH_TBL(hash), key, &val)) {
+ if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) {
return val;
}
- mid = mrb_intern_lit(mrb, "default");
+ mid = MRB_SYM(default);
if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) {
return hash_default(mrb, hash, key);
}
@@ -705,7 +1231,7 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
{
mrb_value val;
- if (sg_get(mrb, RHASH_TBL(hash), key, &val)) {
+ if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) {
return val;
}
/* not found */
@@ -715,36 +1241,11 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
MRB_API void
mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val)
{
- mrb_hash_modify(mrb, hash);
-
- sg_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;
-}
-
-MRB_API mrb_value
-mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash)
-{
- return mrb_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash");
-}
-
-MRB_API mrb_value
-mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
-{
- return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash");
-}
-
-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) = sg_new(mrb);
- }
+ hash_modify(mrb, hash);
+ key = h_key_for(mrb, key);
+ h_set(mrb, mrb_hash_ptr(hash), key, val);
+ mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), key);
+ mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), val);
}
/* 15.2.13.4.16 */
@@ -791,17 +1292,17 @@ mrb_hash_init(mrb_state *mrb, mrb_value hash)
ifnone = mrb_nil_value();
mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p);
- mrb_hash_modify(mrb, hash);
+ hash_modify(mrb, hash);
if (!mrb_nil_p(block)) {
if (ifnone_p) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
+ mrb_argnum_error(mrb, 1, 0, 0);
}
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);
+ mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone);
}
return hash;
}
@@ -823,26 +1324,11 @@ mrb_hash_init(mrb_state *mrb, mrb_value hash)
static mrb_value
mrb_hash_aget(mrb_state *mrb, mrb_value self)
{
- mrb_value key;
+ mrb_value key = mrb_get_arg1(mrb);
- 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:
@@ -875,7 +1361,7 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash)
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);
+ return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key);
}
else {
return RHASH_IFNONE(hash);
@@ -908,11 +1394,10 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash)
static mrb_value
mrb_hash_set_default(mrb_state *mrb, mrb_value hash)
{
- mrb_value ifnone;
+ mrb_value ifnone = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &ifnone);
- mrb_hash_modify(mrb, hash);
- mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
+ hash_modify(mrb, hash);
+ mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone);
RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT;
if (!mrb_nil_p(ifnone)) {
RHASH(hash)->flags |= MRB_HASH_DEFAULT;
@@ -938,7 +1423,6 @@ mrb_hash_set_default(mrb_state *mrb, mrb_value hash)
* a #=> [nil, nil, 4]
*/
-
static mrb_value
mrb_hash_default_proc(mrb_state *mrb, mrb_value hash)
{
@@ -964,11 +1448,10 @@ mrb_hash_default_proc(mrb_state *mrb, mrb_value hash)
static mrb_value
mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
{
- mrb_value ifnone;
+ mrb_value ifnone = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &ifnone);
- mrb_hash_modify(mrb, hash);
- mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
+ hash_modify(mrb, hash);
+ mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone);
if (!mrb_nil_p(ifnone)) {
RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
RHASH(hash)->flags |= MRB_HASH_DEFAULT;
@@ -984,10 +1467,10 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key)
{
- seglist *sg = RHASH_TBL(hash);
mrb_value del_val;
- if (sg_del(mrb, sg, key, &del_val)) {
+ hash_modify(mrb, hash);
+ if (h_delete(mrb, mrb_hash_ptr(hash), key, &del_val)) {
return del_val;
}
@@ -998,40 +1481,11 @@ mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key)
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);
+ mrb_value key = mrb_get_arg1(mrb);
+ mrb->c->ci->mid = 0;
return mrb_hash_delete_key(mrb, self, key);
}
-/* find first element in segment list, and remove it. */
-static void
-sg_shift(mrb_state *mrb, seglist *t, mrb_value *kp, mrb_value *vp)
-{
- segment *seg = t->rootseg;
- mrb_int i;
-
- while (seg) {
- for (i=0; i<MRB_SG_SEGMENT_SIZE; 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:
@@ -1049,25 +1503,19 @@ sg_shift(mrb_state *mrb, seglist *t, mrb_value *kp, mrb_value *vp)
static mrb_value
mrb_hash_shift(mrb_state *mrb, mrb_value hash)
{
- seglist *sg = RHASH_TBL(hash);
+ struct RHash *h = mrb_hash_ptr(hash);
- mrb_hash_modify(mrb, hash);
- if (sg && sg_size(mrb, sg) > 0) {
+ hash_modify(mrb, hash);
+ if (h_size(h) == 0) {
+ return mrb_nil_value();
+ }
+ else {
mrb_value del_key, del_val;
-
- sg_shift(mrb, sg, &del_key, &del_val);
+ h_shift(mrb, h, &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 */
@@ -1085,13 +1533,8 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_clear(mrb_state *mrb, mrb_value hash)
{
- seglist *sg = RHASH_TBL(hash);
-
- mrb_hash_modify(mrb, hash);
- if (sg) {
- sg_free(mrb, sg);
- RHASH_TBL(hash) = NULL;
- }
+ hash_modify(mrb, hash);
+ h_clear(mrb, mrb_hash_ptr(hash));
return hash;
}
@@ -1124,12 +1567,18 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
return val;
}
+MRB_API mrb_int
+mrb_hash_size(mrb_state *mrb, mrb_value hash)
+{
+ return (mrb_int)h_size(mrb_hash_ptr(hash));
+}
+
/* 15.2.13.4.20 */
/* 15.2.13.4.25 */
/*
* call-seq:
- * hsh.length -> fixnum
- * hsh.size -> fixnum
+ * hsh.length -> integer
+ * hsh.size -> integer
*
* Returns the number of key-value pairs in the hash.
*
@@ -1141,19 +1590,14 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_hash_size_m(mrb_state *mrb, mrb_value self)
{
- seglist *sg = RHASH_TBL(self);
-
- if (!sg) return mrb_fixnum_value(0);
- return mrb_fixnum_value(sg_size(mrb, sg));
+ mrb_int size = mrb_hash_size(mrb, self);
+ return mrb_int_value(mrb, size);
}
MRB_API mrb_bool
mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
{
- seglist *sg = RHASH_TBL(self);
-
- if (!sg) return TRUE;
- return sg_size(mrb, sg) == 0;
+ return h_size(mrb_hash_ptr(self)) == 0;
}
/* 15.2.13.4.12 */
@@ -1172,27 +1616,6 @@ mrb_hash_empty_m(mrb_state *mrb, mrb_value self)
return mrb_bool_value(mrb_hash_empty_p(mrb, self));
}
-/* 15.2.13.4.29 (x)*/
-/*
- * call-seq:
- * hsh.to_hash => hsh
- *
- * Returns +self+.
- */
-
-static mrb_value
-mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
-{
- return hash;
-}
-
-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:
@@ -1209,24 +1632,14 @@ hash_keys_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
MRB_API mrb_value
mrb_hash_keys(mrb_state *mrb, mrb_value hash)
{
- seglist *sg = RHASH_TBL(hash);
- size_t size;
- mrb_value ary;
-
- if (!sg || (size = sg_size(mrb, sg)) == 0)
- return mrb_ary_new(mrb);
- ary = mrb_ary_new_capa(mrb, size);
- sg_foreach(mrb, sg, hash_keys_i, (void*)&ary);
+ struct RHash *h = mrb_hash_ptr(hash);
+ mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h));
+ h_each(h, entry, {
+ mrb_ary_push(mrb, ary, entry->key);
+ });
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:
@@ -1243,14 +1656,11 @@ hash_vals_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
MRB_API mrb_value
mrb_hash_values(mrb_state *mrb, mrb_value hash)
{
- seglist *sg = RHASH_TBL(hash);
- size_t size;
- mrb_value ary;
-
- if (!sg || (size = sg_size(mrb, sg)) == 0)
- return mrb_ary_new(mrb);
- ary = mrb_ary_new_capa(mrb, size);
- sg_foreach(mrb, sg, hash_vals_i, (void*)&ary);
+ struct RHash *h = mrb_hash_ptr(hash);
+ mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h));
+ h_each(h, entry, {
+ mrb_ary_push(mrb, ary, entry->val);
+ });
return ary;
}
@@ -1276,43 +1686,20 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash)
MRB_API mrb_bool
mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key)
{
- seglist *sg;
-
- sg = RHASH_TBL(hash);
- if (sg_get(mrb, sg, key, NULL)) {
- return TRUE;
- }
- return FALSE;
+ mrb_value val;
+ return h_get(mrb, mrb_hash_ptr(hash), key, &val);
}
static mrb_value
mrb_hash_has_key(mrb_state *mrb, mrb_value hash)
{
- mrb_value key;
+ mrb_value key = mrb_get_arg1(mrb);
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 */
/*
@@ -1331,43 +1718,33 @@ hash_has_value_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
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;
- sg_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)
-{
- seglist *h1 = (seglist*)data;
-
- sg_put(mrb, h1, key, val);
- return 0;
+ mrb_value val = mrb_get_arg1(mrb);
+ struct RHash *h = mrb_hash_ptr(hash);
+ h_each(h, entry, {
+ h_check_modified(mrb, h, {
+ if (mrb_equal(mrb, val, entry->val)) return mrb_true_value();
+ });
+ });
+ return mrb_false_value();
}
MRB_API void
mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2)
{
- seglist *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) = sg_copy(mrb, h2);
- return;
- }
- sg_foreach(mrb, h2, merge_i, h1);
- mrb_write_barrier(mrb, (struct RBasic*)RHASH(hash1));
- return;
+ struct RHash *h1, *h2;
+
+ hash_modify(mrb, hash1);
+ mrb_ensure_hash_type(mrb, hash2);
+ h1 = mrb_hash_ptr(hash1);
+ h2 = mrb_hash_ptr(hash2);
+
+ if (h1 == h2) return;
+ if (h_size(h2) == 0) return;
+ h_each(h2, entry, {
+ h_check_modified(mrb, h2, {h_set(mrb, h1, entry->key, entry->val);});
+ mrb_field_write_barrier_value(mrb, (struct RBasic *)h1, entry->key);
+ mrb_field_write_barrier_value(mrb, (struct RBasic *)h1, entry->val);
+ });
}
/*
@@ -1378,15 +1755,22 @@ mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2)
* values of key objects have changed since they were inserted, this
* method will reindex <i>hsh</i>.
*
- * h = {"AAA" => "b"}
- * h.keys[0].chop!
- * h.rehash #=> {"AA"=>"b"}
- * h["AA"] #=> "b"
+ * keys = (1..17).map{|n| [n]}
+ * k = keys[0]
+ * h = {}
+ * keys.each{|key| h[key] = key[0]}
+ * h #=> { [1]=>1, [2]=>2, ... [16]=>16, [17]=>17}
+ * h[k] #=> 1
+ * k[0] = keys.size + 1
+ * h #=> {[18]=>1, [2]=>2, ... [16]=>16, [17]=>17}
+ * h[k] #=> nil
+ * h.rehash
+ * h[k] #=> 1
*/
static mrb_value
mrb_hash_rehash(mrb_state *mrb, mrb_value self)
{
- sg_compact(mrb, RHASH_TBL(self));
+ h_rehash(mrb, mrb_hash_ptr(self));
return self;
}
@@ -1398,11 +1782,10 @@ mrb_init_hash(mrb_state *mrb)
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_default, MRB_ARGS_OPT(1)); /* 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 */
@@ -1411,17 +1794,17 @@ mrb_init_hash(mrb_state *mrb)
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, "initialize", mrb_hash_init, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.2.13.4.16 */
+ mrb_define_method(mrb, h, "initialize_copy", mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.17 */
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, "replace", mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.23 */
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());
-
- mrb_define_method(mrb, h, "to_hash", mrb_hash_to_hash, MRB_ARGS_NONE()); /* 15.2.13.4.29 (x)*/
}
diff --git a/src/kernel.c b/src/kernel.c
index db681d510..4dcf6261b 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -13,13 +13,14 @@
#include <mruby/variable.h>
#include <mruby/error.h>
#include <mruby/istruct.h>
+#include <mruby/presym.h>
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;
+ const struct RProc *p;
if (MRB_METHOD_UNDEF_P(m)) return FALSE;
if (MRB_METHOD_FUNC_P(m))
@@ -33,7 +34,7 @@ mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
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);
+ return mrb_func_basic_p(mrb, obj, MRB_SYM(to_s), mrb_any_to_s);
}
/* 15.3.1.3.17 */
@@ -53,7 +54,7 @@ mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
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)) {
+ if (mrb_object_p(obj) && 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);
@@ -71,9 +72,8 @@ mrb_obj_inspect(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_equal_m(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
+ mrb_value arg = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &arg);
return mrb_bool_value(mrb_equal(mrb, self, arg));
}
@@ -97,7 +97,19 @@ mrb_equal_m(mrb_state *mrb, mrb_value self)
mrb_value
mrb_obj_id_m(mrb_state *mrb, mrb_value self)
{
- return mrb_fixnum_value(mrb_obj_id(self));
+ return mrb_int_value(mrb, mrb_obj_id(self));
+}
+
+static int
+env_bidx(struct REnv *e)
+{
+ int bidx;
+
+ /* use saved block arg position */
+ bidx = MRB_ENV_BIDX(e);
+ /* bidx may be useless (e.g. define_method) */
+ if (bidx >= MRB_ENV_LEN(e)) return -1;
+ return bidx;
}
/* 15.3.1.2.2 */
@@ -130,7 +142,9 @@ 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;
+ int bidx;
+ struct REnv *e = NULL;
+ const struct RProc *p;
if (ci <= cibase) {
/* toplevel does not have block */
@@ -140,33 +154,39 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
/* search method/class/module proc */
while (p) {
if (MRB_PROC_SCOPE_P(p)) break;
+ e = MRB_PROC_ENV(p);
p = p->upper;
}
if (p == NULL) return mrb_false_value();
+ if (e) {
+ bidx = env_bidx(e);
+ if (bidx < 0) return mrb_false_value();
+ bp = &e->stack[bidx];
+ goto block_given;
+ }
/* search ci corresponding to proc */
while (cibase < ci) {
if (ci->proc == p) break;
ci--;
}
if (ci == cibase) {
- return mrb_false_value();
+ /* proc is closure */
+ if (!MRB_PROC_ENV_P(p)) return mrb_false_value();
+ e = MRB_PROC_ENV(p);
+ bidx = env_bidx(e);
+ if (bidx < 0) return mrb_false_value();
+ bp = &e->stack[bidx];
}
- else if (ci->env) {
- struct REnv *e = ci->env;
- int bidx;
-
+ else if ((e = mrb_vm_ci_env(ci)) != NULL) {
/* 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);
+ if (e->stack == mrb->c->stbase) return mrb_false_value();
+ bidx = env_bidx(e);
/* bidx may be useless (e.g. define_method) */
- if (bidx >= MRB_ENV_STACK_LEN(e))
- return mrb_false_value();
+ if (bidx < 0) return mrb_false_value();
bp = &e->stack[bidx];
}
else {
- bp = ci[1].stackent+1;
+ bp = ci->stack+1;
if (ci->argc >= 0) {
bp += ci->argc;
}
@@ -174,6 +194,7 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
bp++;
}
}
+ block_given:
if (mrb_nil_p(*bp))
return mrb_false_value();
return mrb_true_value();
@@ -188,7 +209,7 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
* called with an explicit receiver, as <code>class</code> is also a
* reserved word in Ruby.
*
- * 1.class #=> Fixnum
+ * 1.class #=> Integer
* self.class #=> Object
*/
static mrb_value
@@ -197,201 +218,20 @@ 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 <i>obj</i>---the instance variables of
- * <i>obj</i> are copied, but not the objects they reference. Copies
- * the frozen state of <i>obj</i>. See also the discussion
- * under <code>Object#dup</code>.
- *
- * class Klass
- * attr_accessor :str
- * end
- * s1 = Klass.new #=> #<Klass:0x401b3a38>
- * s1.str = "Hello" #=> "Hello"
- * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
- * s2.str[1,4] = "i" #=> "i"
- * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
- * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
- *
- * 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 <i>obj</i>---the instance variables of
- * <i>obj</i> are copied, but not the objects they reference.
- * <code>dup</code> copies the frozen state of <i>obj</i>. See also
- * the discussion under <code>Object#clone</code>. In general,
- * <code>clone</code> and <code>dup</code> may have different semantics
- * in descendant classes. While <code>clone</code> is used to duplicate
- * an object, including its internal state, <code>dup</code> 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_obj_extend(mrb_state *mrb, mrb_int argc, const 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)");
+ mrb_argnum_error(mrb, argc, 1, -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);
+ mrb_funcall_id(mrb, argv[argc], MRB_SYM(extend_object), 1, obj);
+ mrb_funcall_id(mrb, argv[argc], MRB_SYM(extended), 1, obj);
}
return obj;
}
@@ -424,34 +264,22 @@ mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj)
static mrb_value
mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
{
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int argc;
mrb_get_args(mrb, "*", &argv, &argc);
return mrb_obj_extend(mrb, argc, argv, self);
}
-static mrb_value
+MRB_API 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);
+ if (!mrb_immediate_p(self)) {
+ struct RBasic *b = mrb_basic_ptr(self);
+ if (!mrb_frozen_p(b)) {
+ MRB_SET_FROZEN_FLAG(b);
+ if (b->c->tt == MRB_TT_SCLASS) MRB_SET_FROZEN_FLAG(b->c);
+ }
}
return self;
}
@@ -459,26 +287,7 @@ mrb_obj_freeze(mrb_state *mrb, mrb_value 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();
+ return mrb_bool_value(mrb_immediate_p(self) || mrb_frozen_p(mrb_basic_ptr(self)));
}
/* 15.3.1.3.15 */
@@ -486,25 +295,24 @@ mrb_obj_frozen(mrb_state *mrb, mrb_value self)
* call-seq:
* obj.hash -> fixnum
*
- * Generates a <code>Fixnum</code> hash value for this object. This
+ * Generates a <code>Integer</code> hash value for this object. This
* function must have the property that <code>a.eql?(b)</code> implies
* <code>a.hash == b.hash</code>. The hash value is used by class
* <code>Hash</code>. Any hash value that exceeds the capacity of a
- * <code>Fixnum</code> will be truncated before being used.
+ * <code>Integer</code> will be truncated before being used.
*/
-MRB_API mrb_value
+static mrb_value
mrb_obj_hash(mrb_state *mrb, mrb_value self)
{
- return mrb_fixnum_value(mrb_obj_id(self));
+ return mrb_int_value(mrb, mrb_obj_id(self));
}
/* 15.3.1.3.16 */
-static mrb_value
+mrb_value
mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
{
- mrb_value orig;
+ mrb_value orig = mrb_get_arg1(mrb);
- 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");
@@ -512,7 +320,6 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
return self;
}
-
MRB_API mrb_bool
mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
{
@@ -531,11 +338,11 @@ mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
static mrb_value
obj_is_instance_of(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
+ struct RClass *c;
- mrb_get_args(mrb, "C", &arg);
+ mrb_get_args(mrb, "c", &c);
- return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)));
+ return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, c));
}
/* 15.3.1.3.24 */
@@ -568,16 +375,13 @@ obj_is_instance_of(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self)
{
- mrb_value arg;
+ struct RClass *c;
- mrb_get_args(mrb, "C", &arg);
+ mrb_get_args(mrb, "c", &c);
- return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg)));
+ return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, c));
}
-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:
@@ -619,8 +423,8 @@ 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]);
+ mrb->c->ci->mid = 0;
switch (argc) {
case 0:
mrb_raise(mrb, E_RUNTIME_ERROR, "");
@@ -640,16 +444,6 @@ mrb_f_raise(mrb_state *mrb, mrb_value self)
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:
@@ -682,7 +476,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
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));
+ mrb_name_error(mrb, sym, "instance variable %n not defined", sym);
}
return val;
}
@@ -690,7 +484,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
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));
+ mrb_no_method_error(mrb, name, args, "undefined method '%n'", name);
}
/* 15.3.1.3.30 */
@@ -716,7 +510,7 @@ mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
* # ...
* end
* def method_missing(methId)
- * str = methId.id2name
+ * str = methId.to_s
* romanToInt(str)
* end
* end
@@ -726,26 +520,26 @@ mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
* r.xxiii #=> 23
* r.mm #=> 2000
*/
-#ifdef MRB_DEFAULT_METHOD_MISSING
-static mrb_value
+mrb_value
mrb_obj_missing(mrb_state *mrb, mrb_value mod)
{
mrb_sym name;
- mrb_value *a;
+ const mrb_value *a;
mrb_int alen;
+ mrb->c->ci->mid = 0;
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:
@@ -765,45 +559,16 @@ basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
static mrb_value
obj_respond_to(mrb_state *mrb, mrb_value self)
{
- mrb_value mid;
mrb_sym id, rtm_id;
- mrb_bool priv = FALSE, respond_to_p = TRUE;
-
- mrb_get_args(mrb, "o|b", &mid, &priv);
-
- if (mrb_symbol_p(mid)) {
- id = mrb_symbol(mid);
- }
- else {
- mrb_value tmp;
- if (mrb_string_p(mid)) {
- tmp = mrb_check_intern_str(mrb, mid);
- }
- else {
- tmp = mrb_check_string_type(mrb, mid);
- if (mrb_nil_p(tmp)) {
- tmp = mrb_inspect(mrb, mid);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
- }
- tmp = mrb_check_intern_str(mrb, tmp);
- }
- if (mrb_nil_p(tmp)) {
- respond_to_p = FALSE;
- }
- else {
- id = mrb_symbol(tmp);
- }
- }
-
- if (respond_to_p) {
- respond_to_p = basic_obj_respond_to(mrb, self, id, !priv);
- }
+ 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?");
+ rtm_id = MRB_SYM_Q(respond_to_missing);
if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) {
mrb_value args[2], v;
- args[0] = mid;
+ 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));
@@ -815,12 +580,30 @@ obj_respond_to(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
{
- mrb_value v;
+ mrb_value v = mrb_get_arg1(mrb);
mrb_int i, len;
- mrb_sym eqq = mrb_intern_lit(mrb, "===");
- mrb_value ary = mrb_ary_splat(mrb, self);
+ mrb_sym eqq = MRB_OPSYM(eqq);
+ mrb_value ary;
- mrb_get_args(mrb, "o", &v);
+ mrb->c->ci->mid = 0;
+ if (mrb_array_p(self)) {
+ ary = self;
+ }
+ else if (mrb_nil_p(self)) {
+ return mrb_false_value();
+ }
+ else if (!mrb_respond_to(mrb, self, mrb_intern_lit(mrb, "to_a"))) {
+ mrb_value c = mrb_funcall_argv(mrb, self, eqq, 1, &v);
+ if (mrb_test(c)) return mrb_true_value();
+ return mrb_false_value();
+ }
+ else {
+ ary = mrb_funcall(mrb, self, "to_a", 0);
+ if (mrb_nil_p(ary)) {
+ return mrb_funcall_argv(mrb, self, eqq, 1, &v);
+ }
+ mrb_ensure_array_type(mrb, ary);
+ }
len = RARRAY_LEN(ary);
for (i=0; i<len; i++) {
mrb_value c = mrb_funcall_argv(mrb, mrb_ary_entry(ary, i), eqq, 1, &v);
@@ -830,6 +613,7 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
}
mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
+
void
mrb_init_kernel(mrb_state *mrb)
{
@@ -848,11 +632,9 @@ mrb_init_kernel(mrb_state *mrb)
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 */
@@ -861,19 +643,16 @@ mrb_init_kernel(mrb_state *mrb)
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, "respond_to?", obj_respond_to, MRB_ARGS_ARG(1,1)); /* 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, "class_defined?", mrb_krn_class_defined, MRB_ARGS_REQ(1));
+ 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_include_module(mrb, mrb->object_class, mrb->kernel_module);
- mrb_define_alias(mrb, mrb->module_class, "dup", "clone"); /* XXX */
}
diff --git a/src/load.c b/src/load.c
index 54b50b14d..bd0811743 100644
--- a/src/load.c
+++ b/src/load.c
@@ -7,50 +7,74 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <mruby/dump.h>
#include <mruby/irep.h>
#include <mruby/proc.h>
#include <mruby/string.h>
#include <mruby/debug.h>
#include <mruby/error.h>
+#include <mruby/data.h>
+#include <mruby/endian.h>
#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);
-}
+#define DEFINE_READ_IREP_FUNC(funcdecl, basecall) \
+ funcdecl \
+ { \
+ int ai = mrb_gc_arena_save(mrb); \
+ struct RProc *proc = basecall; \
+ struct mrb_irep *irep = (mrb_irep*)(proc ? proc->body.irep : NULL); \
+ if (irep) proc->body.irep = NULL; \
+ mrb_gc_arena_restore(mrb, ai); \
+ return irep; \
+ }
-static size_t
-offset_crc_body(void)
+#ifndef MRB_NO_FLOAT
+static double
+str_to_double(mrb_state *mrb, const char *p)
{
- struct rite_binary_header header;
- return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc);
+ /* dump IEEE754 little endian binary */
+ union {
+ char s[sizeof(double)];
+ double f;
+ } u;
+
+ if (littleendian) {
+ memcpy(u.s, p, sizeof(double));
+ }
+ else {
+ size_t i;
+ for (i=0; i<sizeof(double); i++) {
+ u.s[i] = p[sizeof(double)-i-1];
+ }
+ }
+ return u.f;
}
+#endif
-static mrb_irep*
-read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
+static mrb_bool
+read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp)
{
int i;
const uint8_t *src = bin;
ptrdiff_t diff;
uint16_t tt, pool_data_len, snl;
int plen;
+ mrb_pool_value *pool;
+ mrb_sym *syms;
int ai = mrb_gc_arena_save(mrb);
mrb_irep *irep = mrb_add_irep(mrb);
+ *irepp = irep;
+
/* skip record size */
src += sizeof(uint32_t);
@@ -63,237 +87,213 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
src += sizeof(uint16_t);
/* number of child irep */
- irep->rlen = (size_t)bin_to_uint16(src);
+ irep->rlen = (uint8_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);
+ /* ISEQ BLOCK (and CATCH HANDLER TABLE BLOCK) */
+ irep->clen = bin_to_uint16(src); /* number of catch handler */
+ src += sizeof(uint16_t);
+ irep->ilen = bin_to_uint16(src);
+ src += sizeof(uint16_t);
if (irep->ilen > 0) {
+ size_t data_len = sizeof(mrb_code) * irep->ilen +
+ sizeof(struct mrb_irep_catch_handler) * irep->clen;
+ mrb_static_assert1(sizeof(struct mrb_irep_catch_handler) == 13);
if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) {
- return NULL;
+ return FALSE;
}
- if ((flags & FLAG_SRC_MALLOC) == 0 &&
- (flags & FLAG_BYTEORDER_NATIVE)) {
+ if ((flags & FLAG_SRC_MALLOC) == 0) {
irep->iseq = (mrb_code*)src;
- src += sizeof(mrb_code) * irep->ilen;
irep->flags |= MRB_ISEQ_NO_FREE;
}
else {
- irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen);
- if (flags & FLAG_BYTEORDER_NATIVE) {
- memcpy(irep->iseq, src, sizeof(uint32_t) * irep->ilen);
- src += sizeof(uint32_t) * irep->ilen;
- }
- else if (flags & FLAG_BYTEORDER_BIG) {
- for (i = 0; i < irep->ilen; i++) {
- irep->iseq[i] = (mrb_code)bin_to_uint32(src); /* iseq */
- src += sizeof(uint32_t);
- }
- }
- else {
- for (i = 0; i < irep->ilen; i++) {
- irep->iseq[i] = (mrb_code)bin_to_uint32l(src); /* iseq */
- src += sizeof(uint32_t);
- }
- }
+ void *buf = mrb_malloc(mrb, data_len);
+ irep->iseq = (mrb_code *)buf;
+ memcpy(buf, src, data_len);
}
+ src += data_len;
}
/* POOL BLOCK */
- plen = bin_to_uint32(src); /* number of pool */
- src += sizeof(uint32_t);
+ plen = bin_to_uint16(src); /* number of pool */
+ src += sizeof(uint16_t);
if (plen > 0) {
if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) {
- return NULL;
+ return FALSE;
}
- irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen);
+ irep->pool = pool = (mrb_pool_value*)mrb_calloc(mrb, sizeof(mrb_pool_value), plen);
for (i = 0; i < plen; i++) {
- mrb_value s;
+ mrb_bool st = (flags & FLAG_SRC_MALLOC)==0;
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;
+ case IREP_TT_INT32:
+ {
+ mrb_int v = (int32_t)bin_to_uint32(src);
+ src += sizeof(uint32_t);
+#ifdef MRB_64BIT
+ pool[i].tt = IREP_TT_INT64;
+ pool[i].u.i64 = (int64_t)v;
#else
- irep->pool[i] = mrb_float_p(num)? mrb_float_pool(mrb, mrb_float(num)) : num;
+ pool[i].tt = IREP_TT_INT32;
+ pool[i].u.i32 = v;
#endif
}
break;
+ case IREP_TT_INT64:
+#ifdef MRB_INT64
+ {
+ uint64_t i64 = bin_to_uint32(src);
+ src += sizeof(uint32_t);
+ i64 <<= 32;
+ i64 |= bin_to_uint32(src);
+ src += sizeof(uint32_t);
+ pool[i].tt = tt;
+ pool[i].u.i64 = (int64_t)i64;
+ }
+ break;
+#else
+ return FALSE;
+#endif
+
+ case IREP_TT_BIGINT:
+ pool_data_len = bin_to_uint8(src); /* pool data length */
+ src += sizeof(uint8_t);
+ {
+ char *p;
+ pool[i].tt = IREP_TT_BIGINT;
+ p = (char*)mrb_malloc(mrb, pool_data_len+2);
+ memcpy(p, src, pool_data_len+2);
+ pool[i].u.str = (const char*)p;
+ }
+ src += pool_data_len + 2;
+ break;
-#ifndef MRB_WITHOUT_FLOAT
case IREP_TT_FLOAT:
- irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE));
+#ifndef MRB_NO_FLOAT
+ pool[i].tt = tt;
+ pool[i].u.f = str_to_double(mrb, (const char*)src);
+ src += sizeof(double);
break;
+#else
+ return FALSE; /* MRB_NO_FLOAT */
#endif
- case IREP_TT_STRING:
- irep->pool[i] = mrb_str_pool(mrb, s);
+ case IREP_TT_STR:
+ pool_data_len = bin_to_uint16(src); /* pool data length */
+ src += sizeof(uint16_t);
+ if (st) {
+ pool[i].tt = (pool_data_len<<2) | IREP_TT_SSTR;
+ pool[i].u.str = (const char*)src;
+ }
+ else {
+ char *p;
+ pool[i].tt = (pool_data_len<<2) | IREP_TT_STR;
+ p = (char*)mrb_malloc(mrb, pool_data_len+1);
+ memcpy(p, src, pool_data_len+1);
+ pool[i].u.str = (const char*)p;
+ }
+ src += pool_data_len + 1;
break;
default:
/* should not happen */
- irep->pool[i] = mrb_nil_value();
- break;
+ return FALSE;
}
- irep->plen++;
- mrb_gc_arena_restore(mrb, ai);
+ irep->plen = i+1;
}
}
/* SYMS BLOCK */
- irep->slen = (uint16_t)bin_to_uint32(src); /* syms length */
- src += sizeof(uint32_t);
+ irep->slen = bin_to_uint16(src); /* syms length */
+ src += sizeof(uint16_t);
if (irep->slen > 0) {
if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) {
- return NULL;
+ return FALSE;
}
- irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen);
+ irep->syms = 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;
+ syms[i] = 0;
continue;
}
if (flags & FLAG_SRC_MALLOC) {
- irep->syms[i] = mrb_intern(mrb, (char *)src, snl);
+ syms[i] = mrb_intern(mrb, (char *)src, snl);
}
else {
- irep->syms[i] = mrb_intern_static(mrb, (char *)src, snl);
+ 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;
+ return TRUE;
}
-static mrb_irep*
-read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
+static mrb_bool
+read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp)
{
- mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags);
+ int ai = mrb_gc_arena_save(mrb);
+ mrb_bool readsuccess = read_irep_record_1(mrb, bin, len, flags, irepp);
+ mrb_irep **reps;
int i;
- if (irep == NULL) {
- return NULL;
+ mrb_gc_arena_restore(mrb, ai);
+ if (!readsuccess) {
+ return FALSE;
}
+ reps = (mrb_irep**)mrb_calloc(mrb, (*irepp)->rlen, sizeof(mrb_irep*));
+ (*irepp)->reps = (const mrb_irep**)reps;
+
bin += *len;
- for (i=0; i<irep->rlen; i++) {
+ for (i=0; i<(*irepp)->rlen; i++) {
size_t rlen;
- irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags);
- if (irep->reps[i] == NULL) {
- return NULL;
+ readsuccess = read_irep_record(mrb, bin, &rlen, flags, &reps[i]);
+ mrb_gc_arena_restore(mrb, ai);
+ if (!readsuccess) {
+ return FALSE;
}
bin += rlen;
*len += rlen;
}
- return irep;
+
+ return TRUE;
}
static mrb_irep*
-read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
+read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags, struct RProc **proc)
{
size_t len;
- bin += sizeof(struct rite_section_irep_header);
- return read_irep_record(mrb, bin, &len, flags);
-}
+ /*
+ * This proc object keeps all the data in progress to avoid memory leaks
+ * if something goes wrong while reading irep.
+ */
+ *proc = mrb_proc_new(mrb, NULL);
-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;
- char *fname;
- uint16_t *lines;
-
- *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);
- fname = (char *)mrb_malloc(mrb, fname_len + 1);
- memcpy(fname, bin, fname_len);
- fname[fname_len] = '\0';
- 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;
- }
- lines = (uint16_t *)mrb_malloc(mrb, niseq * sizeof(uint16_t));
- for (i = 0; i < niseq; i++) {
- lines[i] = bin_to_uint16(bin);
- bin += sizeof(uint16_t); /* niseq */
- *len += sizeof(uint16_t);
+ mrb_irep **irepp = (mrb_irep**)&(*proc)->body.irep;
+ bin += sizeof(struct rite_section_irep_header);
+ if (read_irep_record(mrb, bin, &len, flags, irepp)) {
+ return *irepp;
}
-
- irep->filename = fname;
- irep->lines = lines;
- 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;
+ else {
+ return NULL;
}
- 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
@@ -304,26 +304,26 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
size_t record_size;
uint16_t f_idx;
int i;
+ mrb_irep_debug_info *debug;
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;
+ irep->debug_info = debug = (mrb_irep_debug_info*)mrb_calloc(mrb, 1, sizeof(mrb_irep_debug_info));
+ debug->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);
+ debug->flen = bin_to_uint16(bin);
+ debug->files = (mrb_irep_debug_info_file**)mrb_calloc(mrb, irep->debug_info->flen, sizeof(mrb_irep_debug_info*));
bin += sizeof(uint16_t);
- for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
+ for (f_idx = 0; f_idx < debug->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 = (mrb_irep_debug_info_file *)mrb_calloc(mrb, 1, sizeof(*file));
+ debug->files[f_idx] = file;
file->start_pos = bin_to_uint32(bin);
bin += sizeof(uint32_t);
@@ -333,8 +333,6 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
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);
@@ -354,8 +352,8 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
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));
+ file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_calloc(
+ mrb, (size_t)(file->line_entry_count), sizeof(mrb_irep_debug_info_line));
for (l = 0; l < file->line_entry_count; ++l) {
file->lines.flat_map[l].start_pos = bin_to_uint32(bin);
bin += sizeof(uint32_t);
@@ -364,6 +362,12 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
}
} break;
+ case mrb_debug_line_packed_map: {
+ file->lines.packed_map = (uint8_t*)mrb_calloc(mrb, 1, (size_t)file->line_entry_count);
+ memcpy(file->lines.packed_map, bin, file->line_entry_count);
+ bin += file->line_entry_count;
+ } break;
+
default: return MRB_DUMP_GENERAL_FAILURE;
}
}
@@ -379,7 +383,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
size_t len;
int ret;
- ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len);
+ ret = read_debug_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, filenames, filenames_len);
if (ret != MRB_DUMP_OK) return ret;
bin += len;
}
@@ -402,6 +406,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t
int result;
uint16_t filenames_len;
mrb_sym *filenames;
+ mrb_value filenames_obj;
bin = start;
header = (struct rite_section_debug_header *)bin;
@@ -409,7 +414,8 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t
filenames_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
- filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)filenames_len);
+ filenames_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)filenames_len);
+ filenames = (mrb_sym*)RSTRING_PTR(filenames_obj);
for (i = 0; i < filenames_len; ++i) {
uint16_t f_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
@@ -433,7 +439,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t
}
debug_exit:
- mrb_free(mrb, filenames);
+ mrb_str_resize(mrb, filenames_obj, 0);
return result;
}
@@ -441,34 +447,31 @@ 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;
+ mrb_sym *lv;
ptrdiff_t diff;
int i;
- irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1));
+ irep->lv = lv = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (irep->nlocals - 1));
- for (i = 0; i + 1< irep->nlocals; ++i) {
+ 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;
+ lv[i] = 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);
+ lv[i] = syms[sym_idx];
}
- 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);
+ ret = read_lv_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, syms, syms_len);
if (ret != MRB_DUMP_OK) return ret;
bin += len;
}
@@ -491,6 +494,7 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl
int result;
uint32_t syms_len;
mrb_sym *syms;
+ mrb_value syms_obj;
mrb_sym (*intern_func)(mrb_state*, const char*, size_t) =
(flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static;
@@ -500,7 +504,8 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl
syms_len = bin_to_uint32(bin);
bin += sizeof(uint32_t);
- syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)syms_len);
+ syms_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)syms_len);
+ syms = (mrb_sym*)RSTRING_PTR(syms_obj);
for (i = 0; i < syms_len; ++i) {
uint16_t const str_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
@@ -520,77 +525,66 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl
}
lv_exit:
- mrb_free(mrb, syms);
+ mrb_str_resize(mrb, syms_obj, 0);
return result;
}
static int
-read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags)
+read_binary_header(const uint8_t *bin, size_t bufsize, size_t *bin_size, 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;
+ if (bufsize < sizeof(struct rite_binary_header)) {
+ return MRB_DUMP_READ_FAULT;
}
- 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 {
+
+ if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) != 0) {
return MRB_DUMP_INVALID_FILE_HEADER;
}
- if (crc) {
- *crc = bin_to_uint16(header->binary_crc);
+ /* if major version is different, they are incompatible */
+ if (memcmp(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version)) != 0) {
+ return MRB_DUMP_INVALID_FILE_HEADER;
}
+ /* if minor version is different, we can accept the older version */
+ if (memcmp(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version)) > 0) {
+ return MRB_DUMP_INVALID_FILE_HEADER;
+ }
+
*bin_size = (size_t)bin_to_uint32(header->binary_size);
+ if (bufsize < *bin_size) {
+ return MRB_DUMP_READ_FAULT;
+ }
+
return MRB_DUMP_OK;
}
-static mrb_irep*
-read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
+static struct RProc*
+read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags)
{
int result;
+ struct RProc *proc = NULL;
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);
+ result = read_binary_header(bin, bufsize, &bin_size, &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);
+ irep = read_section_irep(mrb, bin, flags, &proc);
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);
@@ -608,43 +602,53 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
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;
+ return proc;
}
-mrb_irep*
-mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
+static struct RProc*
+mrb_proc_read_irep(mrb_state *mrb, const uint8_t *bin)
{
-#ifdef MRB_USE_ETEXT_EDATA
+#if defined(MRB_USE_LINK_TIME_RO_DATA_P) || defined(MRB_USE_CUSTOM_RO_DATA_P)
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);
+ return read_irep(mrb, bin, (size_t)-1, flags);
+}
+
+DEFINE_READ_IREP_FUNC(
+ mrb_irep *mrb_read_irep(mrb_state *mrb, const uint8_t *bin),
+ mrb_proc_read_irep(mrb, bin))
+
+static struct RProc*
+mrb_proc_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
+{
+ return read_irep(mrb, (const uint8_t *)buf, bufsize, FLAG_SRC_MALLOC);
}
+DEFINE_READ_IREP_FUNC(
+ MRB_API mrb_irep *mrb_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize),
+ mrb_proc_read_irep_buf(mrb, buf, bufsize))
+
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"));
+ mrb_exc_set(mrb, mrb_exc_new_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)
+load_irep(mrb_state *mrb, struct RProc *proc, mrbc_context *c)
{
- struct RProc *proc;
-
- if (!irep) {
+ if (!proc || !proc->body.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);
@@ -653,7 +657,15 @@ load_irep(mrb_state *mrb, mrb_irep *irep, mrbc_context *c)
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);
+ struct RProc *proc = mrb_proc_read_irep(mrb, bin);
+ if (!proc) return mrb_undef_value();
+ return load_irep(mrb, proc, c);
+}
+
+MRB_API mrb_value
+mrb_load_irep_buf_cxt(mrb_state *mrb, const void *buf, size_t bufsize, mrbc_context *c)
+{
+ return load_irep(mrb, mrb_proc_read_irep_buf(mrb, buf, bufsize), c);
}
MRB_API mrb_value
@@ -662,12 +674,24 @@ mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
return mrb_load_irep_cxt(mrb, bin, NULL);
}
-#ifndef MRB_DISABLE_STDIO
+MRB_API mrb_value
+mrb_load_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
+{
+ return mrb_load_irep_buf_cxt(mrb, buf, bufsize, NULL);
+}
-mrb_irep*
-mrb_read_irep_file(mrb_state *mrb, FILE* fp)
+MRB_API mrb_value
+mrb_load_proc(mrb_state *mrb, const struct RProc *proc)
{
- mrb_irep *irep = NULL;
+ return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
+}
+
+#ifndef MRB_NO_STDIO
+
+static struct RProc*
+mrb_proc_read_irep_file(mrb_state *mrb, FILE *fp)
+{
+ struct RProc *proc = NULL;
uint8_t *buf;
const size_t header_size = sizeof(struct rite_binary_header);
size_t buf_size = 0;
@@ -682,7 +706,7 @@ mrb_read_irep_file(mrb_state *mrb, FILE* fp)
if (fread(buf, header_size, 1, fp) == 0) {
goto irep_exit;
}
- result = read_binary_header(buf, &buf_size, NULL, &flags);
+ result = read_binary_header(buf, (size_t)-1, &buf_size, &flags);
if (result != MRB_DUMP_OK || buf_size <= header_size) {
goto irep_exit;
}
@@ -691,17 +715,21 @@ mrb_read_irep_file(mrb_state *mrb, FILE* fp)
if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) {
goto irep_exit;
}
- irep = read_irep(mrb, buf, FLAG_SRC_MALLOC);
+ proc = read_irep(mrb, buf, (size_t)-1, FLAG_SRC_MALLOC);
irep_exit:
mrb_free(mrb, buf);
- return irep;
+ return proc;
}
+DEFINE_READ_IREP_FUNC(
+ mrb_irep *mrb_read_irep_file(mrb_state *mrb, FILE *fp),
+ mrb_proc_read_irep_file(mrb, fp))
+
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);
+ return load_irep(mrb, mrb_proc_read_irep_file(mrb, fp), c);
}
MRB_API mrb_value
@@ -709,4 +737,4 @@ mrb_load_irep_file(mrb_state *mrb, FILE* fp)
{
return mrb_load_irep_file_cxt(mrb, fp, NULL);
}
-#endif /* MRB_DISABLE_STDIO */
+#endif /* MRB_NO_STDIO */
diff --git a/src/mruby_core.rake b/src/mruby_core.rake
deleted file mode 100644
index bb3d7b633..000000000
--- a/src/mruby_core.rake
+++ /dev/null
@@ -1,20 +0,0 @@
-MRuby.each_target do
- current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd)
- relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
- current_build_dir = "#{build_dir}/#{relative_from_root}"
-
- 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
-
- file libfile("#{build_dir}/lib/libmruby_core") => objs do |t|
- archiver.run t.name, t.prerequisites
- end
-end
diff --git a/src/numeric.c b/src/numeric.c
index f7f0318e8..b22026ebb 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -1,50 +1,65 @@
/*
-** numeric.c - Numeric, Integer, Float, Fixnum class
+** numeric.c - Numeric, Integer, Float class
**
** See Copyright Notice in mruby.h
*/
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
#include <float.h>
#include <math.h>
#endif
#include <limits.h>
#include <stdlib.h>
+#include <string.h>
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
#include <mruby/class.h>
+#include <mruby/presym.h>
-#ifndef MRB_WITHOUT_FLOAT
-#ifdef MRB_USE_FLOAT
+#ifndef MRB_NO_FLOAT
+#ifdef MRB_USE_FLOAT32
#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
+#ifndef MRB_NO_FLOAT
MRB_API mrb_float
-mrb_to_flo(mrb_state *mrb, mrb_value val)
+mrb_as_float(mrb_state *mrb, mrb_value val)
{
switch (mrb_type(val)) {
- case MRB_TT_FIXNUM:
- return (mrb_float)mrb_fixnum(val);
+ case MRB_TT_INTEGER:
+ return (mrb_float)mrb_integer(val);
case MRB_TT_FLOAT:
break;
- default:
+ case MRB_TT_STRING:
+ case MRB_TT_FALSE:
+ case MRB_TT_TRUE:
mrb_raise(mrb, E_TYPE_ERROR, "non float value");
+ default:
+ val = mrb_type_convert(mrb, val, MRB_TT_FLOAT, MRB_SYM(to_f));
+ break;
}
return mrb_float(val);
}
#endif
+static void
+int_overflow(mrb_state *mrb, const char *reason)
+{
+ mrb_raisef(mrb, E_RANGE_ERROR, "integer overflow in %s", reason);
+}
+
+static void
+int_zerodiv(mrb_state *mrb)
+{
+ mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0");
+}
+
/*
* call-seq:
*
@@ -55,74 +70,93 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
* 2.0**3 #=> 8.0
*/
static mrb_value
-num_pow(mrb_state *mrb, mrb_value x)
+int_pow(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
-#ifndef MRB_WITHOUT_FLOAT
- mrb_float d;
-#endif
+ mrb_int base = mrb_integer(x);
+ mrb_int result = 1;
+ mrb_int exp;
- 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;
+#ifndef MRB_NO_FLOAT
+ mrb_value y = mrb_get_arg1(mrb);
- 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;
+ if (mrb_float_p(y)) {
+ return mrb_float_value(mrb, pow((double)base, mrb_float(y)));
+ }
+ else if (mrb_integer_p(y)) {
+ exp = mrb_integer(y);
+ }
+ else
#endif
- }
- }
- exp >>= 1;
- if (exp == 0) break;
- if (mrb_int_mul_overflow(base, base, &base)) {
-#ifndef MRB_WITHOUT_FLOAT
- goto float_pow;
+ {
+ mrb_get_args(mrb, "i", &exp);
+ }
+ if (exp < 0) {
+#ifndef MRB_NO_FLOAT
+ return mrb_float_value(mrb, pow((double)base, (double)exp));
+#else
+ int_overflow(mrb, "negative power");
#endif
+ }
+ for (;;) {
+ if (exp & 1) {
+ if (mrb_int_mul_overflow(result, base, &result)) {
+ int_overflow(mrb, "power");
}
}
- return mrb_fixnum_value(result);
+ exp >>= 1;
+ if (exp == 0) break;
+ if (mrb_int_mul_overflow(base, base, &base)) {
+ int_overflow(mrb, "power");
+ }
}
-#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
+ return mrb_int_value(mrb, result);
+}
+
+mrb_int
+mrb_div_int(mrb_state *mrb, mrb_int x, mrb_int y)
+{
+ if (y == 0) {
+ int_zerodiv(mrb);
+ }
+ else if(x == MRB_INT_MIN && y == -1) {
+ int_overflow(mrb, "division");
+ }
+ else {
+ mrb_int div = x / y;
+
+ if ((x ^ y) < 0 && x != div * y) {
+ div -= 1;
+ }
+ return div;
+ }
+ /* not reached */
+ return 0;
}
/* 15.2.8.3.4 */
/* 15.2.9.3.4 */
/*
* call-seq:
- * num / other -> num
+ * int / other -> num
*
* Performs division: the class of the resulting object depends on
* the class of <code>num</code> and on the magnitude of the
* result.
*/
-
-mrb_value
-mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+int_div(mrb_state *mrb, mrb_value x)
{
-#ifdef MRB_WITHOUT_FLOAT
- if (!mrb_fixnum_p(y)) {
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a = mrb_integer(x);
+
+ if (mrb_integer_p(y)) {
+ mrb_int div = mrb_div_int(mrb, a, mrb_integer(y));
+ return mrb_int_value(mrb, div);
}
- return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer division");
#else
- return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y));
+ return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
#endif
}
@@ -134,53 +168,199 @@ mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
* Returns most exact division.
*/
+/*
+ * call-seq:
+ * int.div(other) -> int
+ *
+ * Performs division: resulting integer.
+ */
static mrb_value
-num_div(mrb_state *mrb, mrb_value x)
+int_idiv(mrb_state *mrb, mrb_value x)
{
-#ifdef MRB_WITHOUT_FLOAT
- mrb_value y;
+ mrb_int y;
- mrb_get_args(mrb, "o", &y);
- if (!mrb_fixnum_p(y)) {
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+ mrb_get_args(mrb, "i", &y);
+ if (y == 0) {
+ int_zerodiv(mrb);
}
- return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
+ return mrb_fixnum_value(mrb_integer(x) / y);
+}
+
+static mrb_value
+int_quo(mrb_state *mrb, mrb_value xv)
+{
+#ifdef MRB_NO_FLOAT
+ return int_idiv(mrb, xv);
#else
mrb_float y;
mrb_get_args(mrb, "f", &y);
- return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y);
+ if (y == 0) {
+ int_zerodiv(mrb);
+ }
+ return mrb_float_value(mrb, mrb_integer(xv) / y);
#endif
}
-#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+coerce_step_counter(mrb_state *mrb, mrb_value self)
+{
+ mrb_value num, step;
+
+ mrb_get_args(mrb, "oo", &num, &step);
+
+#ifndef MRB_NO_FLOAT
+ mrb->c->ci->mid = 0;
+ if (mrb_float_p(num) || mrb_float_p(step)) {
+ return mrb_to_float(mrb, self);
+ }
+#endif
+
+ return self;
+}
+
+#ifndef MRB_NO_FLOAT
/********************************************************************
*
* Document-class: Float
*
* <code>Float</code> objects represent inexact real numbers using
- * the native architecture's double-precision floating point
+ * the native architecture's double-precision floating-point
* representation.
*/
+static mrb_value
+flo_pow(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float d = pow(mrb_as_float(mrb, x), mrb_as_float(mrb, y));
+ return mrb_float_value(mrb, d);
+}
+
+static mrb_value
+flo_idiv(mrb_state *mrb, mrb_value xv)
+{
+ mrb_int y, div;
+
+ mrb_get_args(mrb, "i", &y);
+ div = mrb_div_int(mrb, (mrb_int)mrb_float(xv), y);
+ return mrb_int_value(mrb, (mrb_int)div);
+}
+
+mrb_float
+mrb_div_float(mrb_float x, mrb_float y)
+{
+ if (y != 0.0) {
+ return x / y;
+ }
+ else if (x == 0.0) {
+ return NAN;
+ }
+ else {
+ return x * (signbit(y) ? -1.0 : 1.0) * INFINITY;
+ }
+}
+
+static mrb_value
+flo_div(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
+
+ if (mrb_float_p(y)) {
+ a = mrb_div_float(a, mrb_float(y));
+ }
+ else {
+ a = mrb_div_float(a, mrb_as_float(mrb, y));
+ }
+ return mrb_float_value(mrb, a);
+}
+
+/* the argument `fmt` is no longer used; you can pass `NULL` */
+mrb_value
+mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
+{
+ char buf[25];
+#ifdef MRB_USE_FLOAT32
+ const int prec = 7;
+#else
+ const int prec = 15;
+#endif
+
+ mrb_format_float(mrb_float(flo), buf, sizeof(buf), 'g', prec, '\0');
+ for (char *p = buf; *p; p++) {
+ if (*p == '.') goto exit;
+ if (*p == 'e') {
+ memmove(p+2, p, strlen(p)+1);
+ memcpy(p, ".0", 2);
+ goto exit;
+ }
+ }
+ strcat(buf, ".0");
+ exit:
+ return mrb_str_new_cstr(mrb, buf);
+}
+
/* 15.2.9.3.16(x) */
/*
* call-seq:
* flt.to_s -> string
+ * flt.inspect -> string
*
* Returns a string containing a representation of self. As well as a
* fixed or exponential form of the number, the call may return
* "<code>NaN</code>", "<code>Infinity</code>", and
* "<code>-Infinity</code>".
+ *
+ * 3.0.to_s #=> 3.0
+ * 3.25.to_s #=> 3.25
*/
static mrb_value
flo_to_s(mrb_state *mrb, mrb_value flt)
{
- if (isnan(mrb_float(flt))) {
- return mrb_str_new_lit(mrb, "NaN");
+ mrb_float f = mrb_float(flt);
+ mrb_value str;
+
+ if (isinf(f)) {
+ str = f < 0 ? mrb_str_new_lit(mrb, "-Infinity")
+ : mrb_str_new_lit(mrb, "Infinity");
+ }
+ else if (isnan(f)) {
+ str = mrb_str_new_lit(mrb, "NaN");
+ }
+ else {
+ str = mrb_float_to_str(mrb, flt, NULL);
+ }
+
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
+}
+
+/* 15.2.9.3.1 */
+/*
+ * call-seq:
+ * float + other -> float
+ *
+ * Returns a new float which is the sum of <code>float</code>
+ * and <code>other</code>.
+ */
+static mrb_value
+flo_add(mrb_state *mrb, mrb_value x)
+{
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
+
+ switch (mrb_type(y)) {
+ case MRB_TT_FLOAT:
+ return mrb_float_value(mrb, a + mrb_float(y));
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x);
+#endif
+ default:
+ return mrb_float_value(mrb, a + mrb_as_float(mrb, y));
}
- return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT);
}
/* 15.2.9.3.2 */
@@ -193,12 +373,22 @@ flo_to_s(mrb_state *mrb, mrb_value flt)
*/
static mrb_value
-flo_minus(mrb_state *mrb, mrb_value x)
+flo_sub(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
- mrb_get_args(mrb, "o", &y);
- return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y));
+ switch (mrb_type(y)) {
+ case MRB_TT_FLOAT:
+ return mrb_float_value(mrb, a - mrb_float(y));
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ x = mrb_funcall_id(mrb, y, MRB_OPSYM(sub), 1, x);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(minus), 0);
+#endif
+ default:
+ return mrb_float_value(mrb, a - mrb_as_float(mrb, y));
+ }
}
/* 15.2.9.3.3 */
@@ -213,10 +403,19 @@ flo_minus(mrb_state *mrb, mrb_value x)
static mrb_value
flo_mul(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_float a = mrb_float(x);
- mrb_get_args(mrb, "o", &y);
- return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
+ switch (mrb_type(y)) {
+ case MRB_TT_FLOAT:
+ return mrb_float_value(mrb, a * mrb_float(y));
+#if defined(MRB_USE_COMPLEX)
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x);
+#endif
+ default:
+ return mrb_float_value(mrb, a * mrb_as_float(mrb, y));
+ }
}
static void
@@ -230,13 +429,9 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
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;
+ int_zerodiv(mrb);
}
- if ((x == 0.0) || (isinf(y) && !isinf(x))) {
+ if (isinf(y) && !isinf(x)) {
mod = x;
}
else {
@@ -249,6 +444,8 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
div = (x - mod) / y;
if (modp && divp) div = round(div);
}
+ if (div == 0) div = 0.0;
+ if (mod == 0) mod = 0.0;
if (y*mod < 0) {
mod += y;
div -= 1.0;
@@ -273,12 +470,10 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
static mrb_value
flo_mod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
mrb_float mod;
- mrb_get_args(mrb, "o", &y);
-
- flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod);
+ flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), 0, &mod);
return mrb_float_value(mrb, mod);
}
#endif
@@ -296,24 +491,22 @@ flo_mod(mrb_state *mrb, mrb_value x)
* (1.0).eql?(1.0) #=> true
*/
static mrb_value
-fix_eql(mrb_state *mrb, mrb_value x)
+int_eql(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- 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));
+ if (!mrb_integer_p(y)) return mrb_false_value();
+ return mrb_bool_value(mrb_integer(x) == mrb_integer(y));
}
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
static mrb_value
flo_eql(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- 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));
+ return mrb_bool_value(mrb_float(x) == mrb_float(y));
}
/* 15.2.9.3.7 */
@@ -332,14 +525,21 @@ flo_eql(mrb_state *mrb, mrb_value x)
static mrb_value
flo_eq(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
- mrb_get_args(mrb, "o", &y);
+ mrb_value y = mrb_get_arg1(mrb);
switch (mrb_type(y)) {
- case MRB_TT_FIXNUM:
- return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y));
+ case MRB_TT_INTEGER:
+ return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_integer(y));
case MRB_TT_FLOAT:
return mrb_bool_value(mrb_float(x) == mrb_float(y));
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ return mrb_bool_value(mrb_float(x) == mrb_as_float(mrb, y));
+#endif
+#ifdef MRB_USE_COMPLEX
+ case MRB_TT_COMPLEX:
+ return mrb_bool_value(mrb_equal(mrb, y, x));
+#endif
default:
return mrb_false_value();
}
@@ -349,11 +549,15 @@ 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_INTEGER:
+ return (int64_t)mrb_integer(x);
case MRB_TT_FLOAT:
- return (int64_t)mrb_float(x);
+ {
+ double f = mrb_float(x);
+
+ if ((mrb_float)INT64_MAX >= f && f >= (mrb_float)INT64_MIN)
+ return (int64_t)f;
+ }
default:
mrb_raise(mrb, E_TYPE_ERROR, "cannot convert to Integer");
break;
@@ -365,29 +569,26 @@ value_int64(mrb_state *mrb, mrb_value x)
static mrb_value
int64_value(mrb_state *mrb, int64_t v)
{
- if (FIXABLE(v)) {
- return mrb_fixnum_value((mrb_int)v);
+ if (!TYPED_FIXABLE(v,int64_t)) {
+ int_overflow(mrb, "bit operation");
}
- return mrb_float_value(mrb, (mrb_float)v);
+ return mrb_fixnum_value((mrb_int)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);
+ int64_t v1 = value_int64(mrb, x);
return int64_value(mrb, ~v1);
}
static mrb_value
flo_and(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
int64_t v1, v2;
- mrb_get_args(mrb, "o", &y);
- v1 = (int64_t)mrb_float(x);
+ v1 = value_int64(mrb, x);
v2 = value_int64(mrb, y);
return int64_value(mrb, v1 & v2);
}
@@ -395,11 +596,10 @@ flo_and(mrb_state *mrb, mrb_value x)
static mrb_value
flo_or(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
int64_t v1, v2;
- mrb_get_args(mrb, "o", &y);
- v1 = (int64_t)mrb_float(x);
+ v1 = value_int64(mrb, x);
v2 = value_int64(mrb, y);
return int64_value(mrb, v1 | v2);
}
@@ -407,11 +607,10 @@ flo_or(mrb_state *mrb, mrb_value x)
static mrb_value
flo_xor(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
int64_t v1, v2;
- mrb_get_args(mrb, "o", &y);
- v1 = (int64_t)mrb_float(x);
+ v1 = value_int64(mrb, x);
v2 = value_int64(mrb, y);
return int64_value(mrb, v1 ^ v2);
}
@@ -425,9 +624,17 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
return x;
}
val = mrb_float(x);
+ if (width < -MRB_INT_BIT/2) {
+ if (val < 0) return mrb_fixnum_value(-1);
+ return mrb_fixnum_value(0);
+ }
if (width < 0) {
while (width++) {
val /= 2;
+ if (val < 1.0) {
+ val = 0;
+ break;
+ }
}
#if defined(_ISOC99_SOURCE)
val = trunc(val);
@@ -447,23 +654,23 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
val *= 2;
}
}
- if (FIXABLE_FLOAT(val)) {
- return mrb_fixnum_value((mrb_int)val);
- }
+ if (FIXABLE_FLOAT(val))
+ return mrb_int_value(mrb, (mrb_int)val);
return mrb_float_value(mrb, val);
}
static mrb_value
-flo_lshift(mrb_state *mrb, mrb_value x)
+flo_rshift(mrb_state *mrb, mrb_value x)
{
mrb_int width;
mrb_get_args(mrb, "i", &width);
+ if (width == MRB_INT_MIN) return flo_shift(mrb, x, -MRB_INT_BIT);
return flo_shift(mrb, x, -width);
}
static mrb_value
-flo_rshift(mrb_state *mrb, mrb_value x)
+flo_lshift(mrb_state *mrb, mrb_value x)
{
mrb_int width;
@@ -537,55 +744,127 @@ mrb_check_num_exact(mrb_state *mrb, mrb_float num)
}
}
+static mrb_value
+flo_ceil_floor(mrb_state *mrb, mrb_value num, double (*func)(double))
+{
+ mrb_float f = mrb_float(num);
+ mrb_int ndigits = 0;
+#ifdef MRB_USE_FLOAT32
+ const int fprec = 7;
+#else
+ const int fprec = 15;
+#endif
+
+ mrb_get_args(mrb, "|i", &ndigits);
+ if (f == 0.0) {
+ return ndigits > 0 ? mrb_float_value(mrb, f) : mrb_fixnum_value(0);
+ }
+ if (ndigits > 0) {
+ if (ndigits > fprec) return num;
+ mrb_float d = pow(10, ndigits);
+ f = func(f * d) / d;
+ return mrb_float_value(mrb, f);
+ }
+ if (ndigits < 0) {
+ mrb_float d = pow(10, -ndigits);
+ f = func(f / d) * d;
+ }
+ else { /* ndigits == 0 */
+ f = func(f);
+ }
+ mrb_check_num_exact(mrb, f);
+ return mrb_int_value(mrb, (mrb_int)f);
+}
+
/* 15.2.9.3.10 */
/*
* call-seq:
- * flt.floor -> integer
+ * float.floor([ndigits]) -> integer or float
*
- * Returns the largest integer less than or equal to <i>flt</i>.
+ * Returns the largest number less than or equal to +float+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
+ *
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* 1.2.floor #=> 1
* 2.0.floor #=> 2
* (-1.2).floor #=> -2
* (-2.0).floor #=> -2
+ *
+ * 1.234567.floor(2) #=> 1.23
+ * 1.234567.floor(3) #=> 1.234
+ * 1.234567.floor(4) #=> 1.2345
+ * 1.234567.floor(5) #=> 1.23456
+ *
+ * 34567.89.floor(-5) #=> 0
+ * 34567.89.floor(-4) #=> 30000
+ * 34567.89.floor(-3) #=> 34000
+ * 34567.89.floor(-2) #=> 34500
+ * 34567.89.floor(-1) #=> 34560
+ * 34567.89.floor(0) #=> 34567
+ * 34567.89.floor(1) #=> 34567.8
+ * 34567.89.floor(2) #=> 34567.89
+ * 34567.89.floor(3) #=> 34567.89
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * (0.3 / 0.1).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);
+ return flo_ceil_floor(mrb, num, floor);
}
/* 15.2.9.3.8 */
/*
* call-seq:
- * flt.ceil -> integer
+ * float.ceil([ndigits]) -> integer or float
+ *
+ * Returns the smallest number greater than or equal to +float+ with
+ * a precision of +ndigits+ decimal digits (default: 0).
+ *
+ * When the precision is negative, the returned value is an integer
+ * with at least <code>ndigits.abs</code> trailing zeros.
*
- * Returns the smallest <code>Integer</code> greater than or equal to
- * <i>flt</i>.
+ * Returns a floating point number when +ndigits+ is positive,
+ * otherwise returns an integer.
*
* 1.2.ceil #=> 2
* 2.0.ceil #=> 2
* (-1.2).ceil #=> -1
* (-2.0).ceil #=> -2
+ *
+ * 1.234567.ceil(2) #=> 1.24
+ * 1.234567.ceil(3) #=> 1.235
+ * 1.234567.ceil(4) #=> 1.2346
+ * 1.234567.ceil(5) #=> 1.23457
+ *
+ * 34567.89.ceil(-5) #=> 100000
+ * 34567.89.ceil(-4) #=> 40000
+ * 34567.89.ceil(-3) #=> 35000
+ * 34567.89.ceil(-2) #=> 34600
+ * 34567.89.ceil(-1) #=> 34570
+ * 34567.89.ceil(0) #=> 34568
+ * 34567.89.ceil(1) #=> 34567.9
+ * 34567.89.ceil(2) #=> 34567.89
+ * 34567.89.ceil(3) #=> 34567.89
+ *
+ * Note that the limited precision of floating point arithmetic
+ * might lead to surprising results:
+ *
+ * (2.1 / 0.7).ceil #=> 4 (!)
*/
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);
+ return flo_ceil_floor(mrb, num, ceil);
}
/* 15.2.9.3.12 */
@@ -594,7 +873,7 @@ flo_ceil(mrb_state *mrb, mrb_value num)
* flt.round([ndigits]) -> integer or float
*
* Rounds <i>flt</i> to a given precision in decimal digits (default 0 digits).
- * Precision may be negative. Returns a floating point number when ndigits
+ * Precision may be negative. Returns a floating-point number when ndigits
* is more than zero.
*
* 1.4.round #=> 1
@@ -636,6 +915,7 @@ flo_round(mrb_state *mrb, mrb_value num)
f = 1.0;
i = ndigits >= 0 ? ndigits : -ndigits;
+ if (ndigits > DBL_DIG+2) return num;
while (--i >= 0)
f = f*10.0;
@@ -666,15 +946,28 @@ flo_round(mrb_state *mrb, mrb_value num)
if (!isfinite(number)) return num;
return mrb_float_value(mrb, number);
}
- return mrb_fixnum_value((mrb_int)number);
+ if (!FIXABLE_FLOAT(number))
+ return mrb_float_value(mrb, number);
+ return mrb_int_value(mrb, (mrb_int)number);
}
/* 15.2.9.3.14 */
+static mrb_value
+flo_to_i(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);
+ return mrb_int_value(mrb, (mrb_int)f);
+}
+
/* 15.2.9.3.15 */
/*
* call-seq:
* flt.to_i -> integer
- * flt.to_int -> integer
* flt.truncate -> integer
*
* Returns <i>flt</i> truncated to an <code>Integer</code>.
@@ -683,16 +976,8 @@ flo_round(mrb_state *mrb, mrb_value num)
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);
+ if (signbit(mrb_float(num))) return flo_ceil(mrb, num);
+ return flo_floor(mrb, num);
}
static mrb_value
@@ -705,8 +990,7 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
/*
* Document-class: Integer
*
- * <code>Integer</code> is the basis for the two concrete classes that
- * hold whole numbers, <code>Bignum</code> and <code>Fixnum</code>.
+ * <code>Integer</code> is hold whole numbers.
*
*/
@@ -714,7 +998,6 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
/*
* call-seq:
* int.to_i -> integer
- * int.to_int -> integer
*
* As <i>int</i> is already an <code>Integer</code>, all these
* methods simply return the receiver.
@@ -726,29 +1009,59 @@ 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)
+static mrb_value
+fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
+ a = mrb_integer(x);
+ if (mrb_integer_p(y)) {
mrb_int b, c;
if (a == 0) return x;
- b = mrb_fixnum(y);
+ b = mrb_integer(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
+ int_overflow(mrb, "multiplication");
}
- return mrb_fixnum_value(c);
+ return mrb_int_value(mrb, c);
}
-#ifdef MRB_WITHOUT_FLOAT
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+ switch (mrb_type(y)) {
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x);
+#endif
+ default:
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication");
#else
- return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
+ return mrb_float_value(mrb, (mrb_float)a * mrb_as_float(mrb, y));
#endif
+ }
+}
+
+MRB_API mrb_value
+mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_integer_p(x)) {
+ return fixnum_mul(mrb, x, y);
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) * mrb_as_float(mrb, y));
+ }
+#endif
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ switch (mrb_type(x)) {
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(mul), 1, y);
+ default:
+ break;
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number multiply");
+ return mrb_nil_value(); /* not reached */
}
/* 15.2.8.3.3 */
@@ -762,40 +1075,33 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
*/
static mrb_value
-fix_mul(mrb_state *mrb, mrb_value x)
+int_mul(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
- return mrb_fixnum_mul(mrb, x, y);
+ return 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);
+ if (y == 0) {
+ int_zerodiv(mrb);
}
- else {
- if (x < 0)
- div = - (-x / y);
- else
- div = x / y;
+ else if(x == MRB_INT_MIN && y == -1) {
+ int_overflow(mrb, "division");
}
- mod = x - div*y;
- if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) {
- mod += y;
- div -= 1;
+ else {
+ mrb_int div = x / y;
+ mrb_int mod = x - div * y;
+
+ if ((x ^ y) < 0 && x != div * y) {
+ mod += y;
+ div -= 1;
+ }
+ if (divp) *divp = div;
+ if (modp) *modp = mod;
}
- if (divp) *divp = div;
- if (modp) *modp = mod;
}
/* 15.2.8.3.5 */
@@ -809,34 +1115,25 @@ fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp)
*/
static mrb_value
-fix_mod(mrb_state *mrb, mrb_value x)
+int_mod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
- mrb_int a;
+ mrb_value y = mrb_get_arg1(mrb);
+ mrb_int a, b;
- mrb_get_args(mrb, "o", &y);
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
- mrb_int b, mod;
+ a = mrb_integer(x);
+ if (mrb_integer_p(y) && a != MRB_INT_MIN && (b=mrb_integer(y)) != MRB_INT_MIN) {
+ mrb_int 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);
+ fixdivmod(mrb, a, b, NULL, &mod);
return mrb_fixnum_value(mod);
}
-#ifdef MRB_WITHOUT_FLOAT
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer modulo");
#else
else {
mrb_float mod;
- flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod);
+ flodivmod(mrb, (mrb_float)a, mrb_as_float(mrb, y), NULL, &mod);
return mrb_float_value(mrb, mod);
}
#endif
@@ -849,55 +1146,44 @@ fix_mod(mrb_state *mrb, mrb_value x)
* See <code>Numeric#divmod</code>.
*/
static mrb_value
-fix_divmod(mrb_state *mrb, mrb_value x)
+int_divmod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
-
- if (mrb_fixnum_p(y)) {
+ if (mrb_integer_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));
+ fixdivmod(mrb, mrb_integer(x), mrb_integer(y), &div, &mod);
+ return mrb_assoc_new(mrb, mrb_int_value(mrb, div), mrb_int_value(mrb, mod));
}
-#ifdef MRB_WITHOUT_FLOAT
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer divmod");
#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);
+ flodivmod(mrb, (mrb_float)mrb_integer(x), mrb_as_float(mrb, y), &div, &mod);
+ a = mrb_int_value(mrb, (mrb_int)div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
#endif
}
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
static mrb_value
flo_divmod(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
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);
+ flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), &div, &mod);
+ if (!FIXABLE_FLOAT(div))
+ a = mrb_float_value(mrb, div);
+ else
+ a = mrb_int_value(mrb, (mrb_int)div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
@@ -916,17 +1202,24 @@ flo_divmod(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_equal(mrb_state *mrb, mrb_value x)
+int_equal(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- 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_INTEGER:
+ return mrb_bool_value(mrb_integer(x) == mrb_integer(y));
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
- return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y));
+ return mrb_bool_value((mrb_float)mrb_integer(x) == mrb_float(y));
+#endif
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ return mrb_bool_value(mrb_equal(mrb, y, x));
+#endif
+#ifdef MRB_USE_COMPLEX
+ case MRB_TT_COMPLEX:
+ return mrb_bool_value(mrb_equal(mrb, y, x));
#endif
default:
return mrb_false_value();
@@ -945,24 +1238,24 @@ fix_equal(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_rev(mrb_state *mrb, mrb_value num)
+int_rev(mrb_state *mrb, mrb_value num)
{
- mrb_int val = mrb_fixnum(num);
+ mrb_int val = mrb_integer(num);
- return mrb_fixnum_value(~val);
+ return mrb_int_value(mrb, ~val);
}
-#ifdef MRB_WITHOUT_FLOAT
+#ifdef MRB_NO_FLOAT
#define bit_op(x,y,op1,op2) do {\
- return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\
+ return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(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)));\
+ if (mrb_integer_p(y)) return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(y))); \
+ return flo_ ## op1(mrb, mrb_float_value(mrb, (mrb_float)mrb_integer(x)));\
} while(0)
#endif
@@ -975,11 +1268,10 @@ static mrb_value flo_xor(mrb_state *mrb, mrb_value x);
*/
static mrb_value
-fix_and(mrb_state *mrb, mrb_value x)
+int_and(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
bit_op(x, y, and, &);
}
@@ -992,11 +1284,10 @@ fix_and(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_or(mrb_state *mrb, mrb_value x)
+int_or(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
bit_op(x, y, or, |);
}
@@ -1009,11 +1300,10 @@ fix_or(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_xor(mrb_state *mrb, mrb_value x)
+int_xor(mrb_state *mrb, mrb_value x)
{
- mrb_value y;
+ mrb_value y = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &y);
bit_op(x, y, or, ^);
}
@@ -1022,61 +1312,34 @@ fix_xor(mrb_state *mrb, mrb_value x)
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
- }
+ mrb_assert(width >= 0);
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
+ int_overflow(mrb, "bit shift");
}
- return mrb_fixnum_value(val << width);
+ return mrb_int_value(mrb, 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;
+ (val <= (MRB_INT_MIN >> width))) {
+ int_overflow(mrb, "bit shift");
}
- return mrb_float_value(mrb, f);
+ return mrb_int_value(mrb, (val * ((mrb_int)1 << width)));
}
-#endif
}
static mrb_value
-rshift(mrb_int val, mrb_int width)
+rshift(mrb_state *mrb, mrb_int val, mrb_int width)
{
- if (width < 0) { /* mrb_int overflow */
- return mrb_fixnum_value(0);
- }
+ mrb_assert(width >= 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);
+ return mrb_int_value(mrb, val >> width);
}
/* 15.2.8.3.12 */
@@ -1088,7 +1351,7 @@ rshift(mrb_int val, mrb_int width)
*/
static mrb_value
-fix_lshift(mrb_state *mrb, mrb_value x)
+int_lshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
@@ -1096,10 +1359,11 @@ fix_lshift(mrb_state *mrb, mrb_value x)
if (width == 0) {
return x;
}
- val = mrb_fixnum(x);
+ val = mrb_integer(x);
if (val == 0) return x;
if (width < 0) {
- return rshift(val, -width);
+ if (width == MRB_INT_MIN) return rshift(mrb, val, MRB_INT_BIT);
+ return rshift(mrb, val, -width);
}
return lshift(mrb, val, width);
}
@@ -1113,7 +1377,7 @@ fix_lshift(mrb_state *mrb, mrb_value x)
*/
static mrb_value
-fix_rshift(mrb_state *mrb, mrb_value x)
+int_rshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
@@ -1121,12 +1385,13 @@ fix_rshift(mrb_state *mrb, mrb_value x)
if (width == 0) {
return x;
}
- val = mrb_fixnum(x);
+ val = mrb_integer(x);
if (val == 0) return x;
if (width < 0) {
+ if (width == MRB_INT_MIN) int_overflow(mrb, "bit shift");
return lshift(mrb, val, -width);
}
- return rshift(val, width);
+ return rshift(mrb, val, width);
}
/* 15.2.8.3.23 */
@@ -1138,11 +1403,11 @@ fix_rshift(mrb_state *mrb, mrb_value x)
*
*/
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
static mrb_value
-fix_to_f(mrb_state *mrb, mrb_value num)
+int_to_f(mrb_state *mrb, mrb_value num)
{
- return mrb_float_value(mrb, (mrb_float)mrb_fixnum(num));
+ return mrb_float_value(mrb, (mrb_float)mrb_integer(num));
}
/*
@@ -1152,7 +1417,7 @@ fix_to_f(mrb_state *mrb, mrb_value num)
* (in particular infinite or NaN)
* to numerical classes which don't support them.
*
- * Float::INFINITY.to_r
+ * Float::INFINITY.to_i
*
* <em>raises the exception:</em>
*
@@ -1160,57 +1425,81 @@ fix_to_f(mrb_state *mrb, mrb_value num)
*/
/* ------------------------------------------------------------------------*/
MRB_API mrb_value
-mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
+mrb_float_to_integer(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");
- }
+ mrb_check_num_exact(mrb, d);
if (FIXABLE_FLOAT(d)) {
z = (mrb_int)d;
}
else {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x);
+ mrb_raisef(mrb, E_RANGE_ERROR, "number (%v) too big for integer", x);
}
}
- return mrb_fixnum_value(z);
+ return mrb_int_value(mrb, z);
}
#endif
-mrb_value
-mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+int_plus(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
+ a = mrb_integer(x);
+ if (mrb_integer_p(y)) {
mrb_int b, c;
if (a == 0) return y;
- b = mrb_fixnum(y);
+ b = mrb_integer(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
+ int_overflow(mrb, "addition");
}
- return mrb_fixnum_value(c);
+ return mrb_int_value(mrb, c);
}
-#ifdef MRB_WITHOUT_FLOAT
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+ switch (mrb_type(y)) {
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x);
+#endif
+ default:
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer addition");
#else
- return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y));
+ return mrb_float_value(mrb, (mrb_float)a + mrb_as_float(mrb, y));
#endif
+ }
+}
+
+MRB_API mrb_value
+mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_integer_p(x)) {
+ return int_plus(mrb, x, y);
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) + mrb_as_float(mrb, y));
+ }
+#endif
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ switch (mrb_type(x)) {
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(add), 1, y);
+ default:
+ break;
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number addition");
+ return mrb_nil_value(); /* not reached */
}
/* 15.2.8.3.1 */
@@ -1223,36 +1512,66 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
* result.
*/
static mrb_value
-fix_plus(mrb_state *mrb, mrb_value self)
+int_add(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_plus(mrb, self, other);
+ return int_plus(mrb, self, other);
}
-mrb_value
-mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+int_minus(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
- a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
+ a = mrb_integer(x);
+ if (mrb_integer_p(y)) {
mrb_int b, c;
- b = mrb_fixnum(y);
+ b = mrb_integer(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
+ int_overflow(mrb, "subtraction");
}
- return mrb_fixnum_value(c);
+ return mrb_int_value(mrb, c);
}
-#ifdef MRB_WITHOUT_FLOAT
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+ switch (mrb_type(y)) {
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ x = mrb_funcall_id(mrb, y, MRB_OPSYM(sub), 1, x);
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(minus), 0);
+#endif
+ default:
+#ifdef MRB_NO_FLOAT
+ mrb_raise(mrb, E_TYPE_ERROR, "non integer subtraction");
#else
- return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y));
+ return mrb_float_value(mrb, (mrb_float)a - mrb_as_float(mrb, y));
#endif
+ }
+}
+
+MRB_API mrb_value
+mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_integer_p(x)) {
+ return int_minus(mrb, x, y);
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) - mrb_as_float(mrb, y));
+ }
+#endif
+#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX)
+ switch (mrb_type(x)) {
+ case MRB_TT_RATIONAL:
+ case MRB_TT_COMPLEX:
+ return mrb_funcall_id(mrb, x, MRB_OPSYM(sub), 1, y);
+ default:
+ break;
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number subtraction");
+ return mrb_nil_value(); /* not reached */
}
/* 15.2.8.3.2 */
@@ -1266,42 +1585,60 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
* result.
*/
static mrb_value
-fix_minus(mrb_state *mrb, mrb_value self)
+int_sub(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_minus(mrb, self, other);
+ return int_minus(mrb, self, other);
}
-
-MRB_API mrb_value
-mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
+MRB_API char*
+mrb_int_to_cstr(char *buf, size_t len, mrb_int n, mrb_int base)
{
- char buf[MRB_INT_BIT+1];
- char *b = buf + sizeof buf;
- mrb_int val = mrb_fixnum(x);
+ char *bufend = buf + len;
+ char *b = bufend-1;
- if (base < 2 || 36 < base) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
- }
+ if (base < 2 || 36 < base) return NULL;
+ if (len < 2) return NULL;
- if (val == 0) {
- *--b = '0';
+ if (n == 0) {
+ buf[0] = '0';
+ buf[1] = '\0';
+ return buf;
}
- else if (val < 0) {
+
+ *b = '\0';
+ if (n < 0) {
do {
- *--b = mrb_digitmap[-(val % base)];
- } while (val /= base);
- *--b = '-';
+ if (b-- == buf) return NULL;
+ *b = mrb_digitmap[-(n % base)];
+ } while (n /= base);
+ if (b-- == buf) return NULL;
+ *b = '-';
}
else {
do {
- *--b = mrb_digitmap[(int)(val % base)];
- } while (val /= base);
+ if (b-- == buf) return NULL;
+ *b = mrb_digitmap[(int)(n % base)];
+ } while (n /= base);
}
+ return b;
+}
- return mrb_str_new(mrb, b, buf + sizeof(buf) - b);
+MRB_API mrb_value
+mrb_integer_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
+{
+ char buf[MRB_INT_BIT+1];
+ mrb_int val = mrb_integer(x);
+
+ if (base < 2 || 36 < base) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base);
+ }
+ const char *p = mrb_int_to_cstr(buf, sizeof(buf), val, base);
+ mrb_assert(p != NULL);
+ mrb_value str = mrb_str_new_cstr(mrb, p);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
/* 15.2.8.3.25 */
@@ -1321,41 +1658,46 @@ mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
*
*/
static mrb_value
-fix_to_s(mrb_state *mrb, mrb_value self)
+int_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);
+ return mrb_integer_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
+#ifdef MRB_NO_FLOAT
mrb_int x, y;
#else
mrb_float x, y;
#endif
-#ifdef MRB_WITHOUT_FLOAT
- x = mrb_fixnum(v1);
+#ifdef MRB_NO_FLOAT
+ x = mrb_integer(v1);
#else
- x = mrb_to_flo(mrb, v1);
+ x = mrb_as_float(mrb, v1);
#endif
switch (mrb_type(v2)) {
- case MRB_TT_FIXNUM:
-#ifdef MRB_WITHOUT_FLOAT
- y = mrb_fixnum(v2);
+ case MRB_TT_INTEGER:
+#ifdef MRB_NO_FLOAT
+ y = mrb_integer(v2);
#else
- y = (mrb_float)mrb_fixnum(v2);
+ y = (mrb_float)mrb_integer(v2);
#endif
break;
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
y = mrb_float(v2);
break;
+#ifdef MRB_USE_RATIONAL
+ case MRB_TT_RATIONAL:
+ y = mrb_as_float(mrb, v2);
+ break;
+#endif
#endif
default:
return -2;
@@ -1372,41 +1714,38 @@ cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
/* 15.2.9.3.6 */
/*
* call-seq:
- * self.f <=> other.f => -1, 0, +1
+ * self.f <=> other.f => -1, 0, +1, or nil
* < => -1
* = => 0
* > => +1
* Comparison---Returns -1, 0, or +1 depending on whether <i>fix</i> is
* less than, equal to, or greater than <i>numeric</i>. This is the
- * basis for the tests in <code>Comparable</code>.
+ * basis for the tests in <code>Comparable</code>. When the operands are
+ * not comparable, it returns nil instead of raising an exception.
*/
static mrb_value
num_cmp(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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
+static mrb_noreturn 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)));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %t with %t failed", v1, v2);
}
static mrb_value
num_lt(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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();
@@ -1416,10 +1755,9 @@ num_lt(mrb_state *mrb, mrb_value self)
static mrb_value
num_le(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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();
@@ -1429,10 +1767,9 @@ num_le(mrb_state *mrb, mrb_value self)
static mrb_value
num_gt(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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();
@@ -1442,131 +1779,135 @@ num_gt(mrb_state *mrb, mrb_value self)
static mrb_value
num_ge(mrb_state *mrb, mrb_value self)
{
- mrb_value other;
+ mrb_value other = mrb_get_arg1(mrb);
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();
}
+MRB_API mrb_int
+mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
+{
+ mrb_value v;
+
+ switch (mrb_type(obj1)) {
+ case MRB_TT_INTEGER:
+ case MRB_TT_FLOAT:
+ return cmpnum(mrb, obj1, obj2);
+ case MRB_TT_STRING:
+ if (!mrb_string_p(obj2))
+ return -2;
+ return mrb_str_cmp(mrb, obj1, obj2);
+ default:
+ v = mrb_funcall_id(mrb, obj1, MRB_OPSYM(cmp), 1, obj2);
+ if (mrb_nil_p(v) || !mrb_integer_p(v))
+ return -2;
+ return mrb_integer(v);
+ }
+}
+
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 <code>float</code>
- * and <code>other</code>.
- */
-#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 *numeric, *integer;
+#ifndef MRB_NO_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->integer_class = integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */
+ MRB_SET_INSTANCE_TT(integer, MRB_TT_INTEGER);
mrb_undef_class_method(mrb, integer, "new");
+ mrb_define_method(mrb, integer, "**", int_pow, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
+ mrb_define_method(mrb, integer, "<", num_lt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, "<=", num_le, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, ">", num_gt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, ">=", num_ge, MRB_ARGS_REQ(1));
+
mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
-#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 */
+ mrb_define_method(mrb, integer, "+", int_add, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
+ mrb_define_method(mrb, integer, "-", int_sub, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
+ mrb_define_method(mrb, integer, "*", int_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
+ mrb_define_method(mrb, integer, "%", int_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */
+ mrb_define_method(mrb, integer, "/", int_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.6 */
+ mrb_define_method(mrb, integer, "quo", int_quo, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+ mrb_define_method(mrb, integer, "div", int_idiv, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integer, "==", int_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */
+ mrb_define_method(mrb, integer, "~", int_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */
+ mrb_define_method(mrb, integer, "&", int_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */
+ mrb_define_method(mrb, integer, "|", int_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
+ mrb_define_method(mrb, integer, "^", int_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
+ mrb_define_method(mrb, integer, "<<", int_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
+ mrb_define_method(mrb, integer, ">>", int_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
+ mrb_define_method(mrb, integer, "eql?", int_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
+#ifndef MRB_NO_FLOAT
+ mrb_define_method(mrb, integer, "to_f", int_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) */
+ mrb_define_method(mrb, integer, "to_s", int_to_s, MRB_ARGS_OPT(1)); /* 15.2.8.3.25 */
+ mrb_define_method(mrb, integer, "inspect", int_to_s, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, integer, "divmod", int_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
+ mrb_define_method(mrb, integer, "__coerce_step_counter", coerce_step_counter, MRB_ARGS_REQ(2));
+
+ /* Fixnum Class for compatibility */
+ mrb_define_const(mrb, mrb->object_class, "Fixnum", mrb_obj_value(integer));
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_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_pow, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "/", flo_div, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */
+ mrb_define_method(mrb, fl, "quo", flo_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+ mrb_define_method(mrb, fl, "div", flo_idiv, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "+", flo_add, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */
+ mrb_define_method(mrb, fl, "-", flo_sub, MRB_ARGS_REQ(1)); /* 15.2.9.3.4 */
+ mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */
+ mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */
+ mrb_define_method(mrb, fl, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
+ mrb_define_method(mrb, fl, "<", num_lt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "<=", num_le, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">", num_gt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">=", num_ge, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
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, ">>", flo_rshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "<<", flo_lshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_OPT(1)); /* 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, "floor", flo_floor, MRB_ARGS_OPT(1)); /* 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, "to_int", flo_truncate, MRB_ARGS_NONE());
- mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */
+ mrb_define_method(mrb, fl, "to_i", flo_to_i, MRB_ARGS_NONE()); /* 15.2.9.3.14 */
+ mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_OPT(1)); /* 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 */
@@ -1575,11 +1916,10 @@ mrb_init_numeric(mrb_state *mrb)
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));
+ mrb_define_const_id(mrb, fl, MRB_SYM(INFINITY), mrb_float_value(mrb, INFINITY));
#endif
#ifdef NAN
- mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
+ mrb_define_const_id(mrb, fl, MRB_SYM(NAN), mrb_float_value(mrb, NAN));
#endif
#endif
- mrb_define_module(mrb, "Integral");
}
diff --git a/src/object.c b/src/object.c
index 8724c5416..8fe4688ac 100644
--- a/src/object.c
+++ b/src/object.c
@@ -9,6 +9,7 @@
#include <mruby/numeric.h>
#include <mruby/string.h>
#include <mruby/class.h>
+#include <mruby/presym.h>
MRB_API mrb_bool
mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
@@ -19,12 +20,12 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
return TRUE;
case MRB_TT_FALSE:
- case MRB_TT_FIXNUM:
- return (mrb_fixnum(v1) == mrb_fixnum(v2));
+ case MRB_TT_INTEGER:
+ return (mrb_integer(v1) == mrb_integer(v2));
case MRB_TT_SYMBOL:
return (mrb_symbol(v1) == mrb_symbol(v2));
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return (mrb_float(v1) == mrb_float(v2));
#endif
@@ -47,7 +48,18 @@ 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);
+#ifndef MRB_NO_FLOAT
+ /* value mixing with integer and float */
+ if (mrb_integer_p(obj1)) {
+ if (mrb_float_p(obj2) && (mrb_float)mrb_integer(obj1) == mrb_float(obj2))
+ return TRUE;
+ }
+ else if (mrb_float_p(obj1)) {
+ if (mrb_integer_p(obj2) && mrb_float(obj1) == (mrb_float)mrb_integer(obj2))
+ return TRUE;
+ }
+#endif
+ result = mrb_funcall_id(mrb, obj1, MRB_OPSYM(eq), 1, obj2);
if (mrb_test(result)) return TRUE;
return FALSE;
}
@@ -83,13 +95,17 @@ mrb_true(mrb_state *mrb, mrb_value obj)
static mrb_value
nil_to_s(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new(mrb, 0, 0);
+ mrb_value str = mrb_str_new_frozen(mrb, NULL, 0);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
static mrb_value
nil_inspect(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new_lit(mrb, "nil");
+ mrb_value str = mrb_str_new_lit_frozen(mrb, "nil");
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
/***********************************************************************
@@ -150,7 +166,9 @@ true_xor(mrb_state *mrb, mrb_value obj)
static mrb_value
true_to_s(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new_lit(mrb, "true");
+ mrb_value str = mrb_str_new_lit_frozen(mrb, "true");
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
/* 15.2.5.3.4 */
@@ -257,7 +275,9 @@ false_or(mrb_state *mrb, mrb_value obj)
static mrb_value
false_to_s(mrb_state *mrb, mrb_value obj)
{
- return mrb_str_new_lit(mrb, "false");
+ mrb_value str = mrb_str_new_lit_frozen(mrb, "false");
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
void
@@ -268,7 +288,7 @@ mrb_init_object(mrb_state *mrb)
struct RClass *f;
mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class);
- MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE);
+ MRB_SET_INSTANCE_TT(n, MRB_TT_FALSE);
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 */
@@ -287,7 +307,7 @@ mrb_init_object(mrb_state *mrb)
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_SET_INSTANCE_TT(f, MRB_TT_FALSE);
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 */
@@ -296,136 +316,86 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, f, "inspect", false_to_s, MRB_ARGS_NONE());
}
-static mrb_value
-inspect_type(mrb_state *mrb, mrb_value val)
+static const char*
+type_name(enum mrb_vtype t)
{
- 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));
+ switch (t) {
+#define MRB_VTYPE_NAME(tt, type, name) case tt: return name;
+ MRB_VTYPE_FOREACH(MRB_VTYPE_NAME)
+#undef MRB_VTYPE_NAME
+ default: return NULL;
}
}
static mrb_value
-convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise)
+convert_type(mrb_state *mrb, mrb_value val, const char *tname, mrb_sym method, mrb_bool raise)
{
- mrb_sym m = 0;
-
- m = mrb_intern_cstr(mrb, method);
- if (!mrb_respond_to(mrb, val, m)) {
+ if (!mrb_respond_to(mrb, val, method)) {
if (raise) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname));
+ if (tname) mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into %s", val, tname);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y", val);
}
return mrb_nil_value();
}
- return mrb_funcall_argv(mrb, val, m, 0, 0);
+ return mrb_funcall_argv(mrb, val, method, 0, 0);
}
MRB_API mrb_value
-mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method)
-{
- mrb_value v;
-
- if (mrb_fixnum_p(val)) return val;
- v = convert_type(mrb, val, "Integer", method, FALSE);
- if (mrb_nil_p(v) || !mrb_fixnum_p(v)) {
- return mrb_nil_value();
- }
- return v;
-}
-
-MRB_API mrb_value
-mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
+mrb_type_convert(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method)
{
mrb_value v;
+ const char *tname;
if (mrb_type(val) == type) return val;
+ tname = type_name(type);
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));
+ if (type == MRB_TT_STRING) return mrb_any_to_s(mrb, val);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be converted to %s by #%n", val, tname, 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_type_convert_check(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym 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);
+ v = convert_type(mrb, val, type_name(type), 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)));
+ enum mrb_vtype xt = mrb_type(x);
+ const char *tname, *ename;
+
+ if (t == xt) return;
+
+ tname = type_name(t);
+ if (mrb_nil_p(x)) {
+ ename = "nil";
+ }
+ else if (mrb_integer_p(x)) {
+ ename = "Integer";
+ }
+ else if (mrb_symbol_p(x)) {
+ ename = "Symbol";
}
+ else if (mrb_immediate_p(x)) {
+ ename = RSTRING_PTR(mrb_obj_as_string(mrb, x));
+ }
+ else {
+ ename = mrb_obj_classname(mrb, x);
+ }
+ if (tname) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
+ ename, tname);
+ }
+ mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %d (%s given)", t, ename);
}
/* 15.3.1.3.46 */
@@ -447,8 +417,10 @@ mrb_any_to_s(mrb_state *mrb, mrb_value 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)));
+ if (!mrb_immediate_p(obj)) {
+ mrb_str_cat_lit(mrb, str, ":");
+ mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
+ }
mrb_str_cat_lit(mrb, str, ">");
return str;
@@ -505,25 +477,22 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
return FALSE;
}
-static mrb_value
-mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method)
-{
- mrb_value v;
-
- if (mrb_fixnum_p(val)) return val;
- v = convert_type(mrb, val, "Integer", method, TRUE);
- if (!mrb_obj_is_kind_of(mrb, v, mrb->fixnum_class)) {
- mrb_value type = inspect_type(mrb, val);
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)",
- type, type, mrb_str_new_cstr(mrb, method), inspect_type(mrb, v));
- }
- return v;
-}
-
MRB_API mrb_value
mrb_to_int(mrb_state *mrb, mrb_value val)
{
- return mrb_to_integer(mrb, val, "to_int");
+
+ if (!mrb_integer_p(val)) {
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(val)) {
+ return mrb_float_to_integer(mrb, val);
+ }
+#endif
+ if (mrb_string_p(val)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert String to Integer");
+ }
+ return mrb_type_convert(mrb, val, MRB_TT_INTEGER, MRB_SYM(to_i));
+ }
+ return val;
}
MRB_API mrb_value
@@ -533,22 +502,16 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
if (mrb_nil_p(val)) {
if (base != 0) goto arg_error;
- mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
}
switch (mrb_type(val)) {
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
if (base != 0) goto arg_error;
- else {
- mrb_float f = mrb_float(val);
- if (FIXABLE_FLOAT(f)) {
- break;
- }
- }
- return mrb_flo_to_fixnum(mrb, val);
+ return mrb_float_to_integer(mrb, val);
#endif
- case MRB_TT_FIXNUM:
+ case MRB_TT_INTEGER:
if (base != 0) goto arg_error;
return val;
@@ -568,29 +531,20 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
arg_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
}
- tmp = convert_type(mrb, val, "Integer", "to_int", FALSE);
- if (mrb_nil_p(tmp) || !mrb_fixnum_p(tmp)) {
- tmp = mrb_to_integer(mrb, val, "to_i");
- }
- return tmp;
+ /* to raise TypeError */
+ return mrb_to_int(mrb, val);
}
+#ifndef MRB_NO_FLOAT
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)
+mrb_to_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_INTEGER:
+ return mrb_float_value(mrb, (mrb_float)mrb_integer(val));
case MRB_TT_FLOAT:
return val;
@@ -599,20 +553,81 @@ mrb_Float(mrb_state *mrb, mrb_value val)
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");
+ return mrb_type_convert(mrb, val, MRB_TT_FLOAT, MRB_SYM(to_f));
}
}
#endif
MRB_API mrb_value
+mrb_to_str(mrb_state *mrb, mrb_value val)
+{
+ return mrb_ensure_string_type(mrb, 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, "%Y cannot be converted to String", 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, "%Y cannot be converted to Array", 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, "%Y cannot be converted to Hash", 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));
+ return mrb_obj_as_string(mrb, mrb_funcall_id(mrb, obj, MRB_SYM(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));
+ return mrb_test(mrb_funcall_id(mrb, obj1, MRB_SYM_Q(eql), 1, obj2));
}
diff --git a/src/pool.c b/src/pool.c
index b87d2cfaa..ab30be1d8 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -4,8 +4,6 @@
** See Copyright Notice in mruby.h
*/
-#include <stddef.h>
-#include <stdint.h>
#include <string.h>
#include <mruby.h>
diff --git a/src/print.c b/src/print.c
index 03b5eadfa..4af871b43 100644
--- a/src/print.c
+++ b/src/print.c
@@ -7,41 +7,89 @@
#include <mruby.h>
#include <mruby/string.h>
#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/presym.h>
+#include <string.h>
+
+#ifndef MRB_NO_STDIO
+static void
+printcstr(const char *str, size_t len, FILE *stream)
+{
+ if (str) {
+ fwrite(str, len, 1, stream);
+ putc('\n', stream);
+ }
+}
-#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);
+ printcstr(RSTRING_PTR(obj), RSTRING_LEN(obj), stream);
}
}
-#else
-# define printstr(obj, stream) (void)0
-#endif
+
+void
+mrb_core_init_printabort(void)
+{
+ static const char *str = "Failed mruby core initialization";
+ printcstr(str, strlen(str), stdout);
+}
MRB_API void
mrb_p(mrb_state *mrb, mrb_value obj)
{
- printstr(mrb_inspect(mrb, obj), stdout);
+ if (mrb_type(obj) == MRB_TT_EXCEPTION && mrb_obj_ptr(obj) == mrb->nomem_err) {
+ static const char *str = "Out of memory";
+ printcstr(str, strlen(str), stdout);
+ }
+ else {
+ 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);
+ printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_DESCRIPTION)), stdout);
+}
+
+MRB_API void
+mrb_show_copyright(mrb_state *mrb)
+{
+ printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_COPYRIGHT)), stdout);
+}
+
+#else
+void
+mrb_core_init_printabort(void)
+{
+}
+
+MRB_API void
+mrb_p(mrb_state *mrb, mrb_value obj)
+{
+}
+
+MRB_API void
+mrb_print_error(mrb_state *mrb)
+{
+}
+
+MRB_API void
+mrb_show_version(mrb_state *mrb)
+{
}
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);
}
+#endif
diff --git a/src/proc.c b/src/proc.c
index 05b897480..1d5a4aa76 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -8,52 +8,88 @@
#include <mruby/class.h>
#include <mruby/proc.h>
#include <mruby/opcode.h>
+#include <mruby/data.h>
+#include <mruby/presym.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
-static mrb_code call_iseq[] = {
+static const mrb_code call_iseq[] = {
OP_CALL,
};
+static const mrb_irep call_irep = {
+ 0, /* nlocals */
+ 2, /* nregs */
+ 0, /* clen */
+ MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE, /* flags */
+ call_iseq, /* iseq */
+ NULL, /* pool */
+ NULL, /* syms */
+ NULL, /* reps */
+ NULL, /* lv */
+ NULL, /* debug_info */
+ 1, /* ilen */
+ 0, /* plen */
+ 0, /* slen */
+ 1, /* rlen */
+ 0, /* refcnt */
+};
+
+static const struct RProc call_proc = {
+ NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT,
+ { &call_irep }, NULL, { NULL }
+};
+
struct RProc*
-mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
+mrb_proc_new(mrb_state *mrb, const 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);
+ p = 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 (ci->proc->color != MRB_GC_RED) {
+ tc = MRB_PROC_TARGET_CLASS(ci->proc);
+ }
+ else {
+ tc = mrb_vm_ci_target_class(ci);
+ if (tc && tc->tt == MRB_TT_ICLASS) {
+ tc = tc->c;
+ }
+ }
}
if (tc == NULL) {
- tc = ci->target_class;
+ tc = mrb_vm_ci_target_class(ci);
}
p->upper = ci->proc;
p->e.target_class = tc;
}
p->body.irep = irep;
- mrb_irep_incref(mrb, irep);
+ if (irep) {
+ mrb_irep_incref(mrb, (mrb_irep*)irep);
+ }
return p;
}
-static struct REnv*
-env_new(mrb_state *mrb, mrb_int nlocals)
+struct REnv*
+mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc)
{
struct REnv *e;
- mrb_callinfo *ci = mrb->c->ci;
- int bidx;
+ mrb_int bidx;
- e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL);
- MRB_ENV_SET_STACK_LEN(e, nlocals);
+ e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, tc);
+ MRB_ENV_SET_LEN(e, nstacks);
bidx = ci->argc;
- if (ci->argc < 0) bidx = 2;
+ if (bidx < 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;
+ e->stack = stack;
+ e->cxt = c;
return e;
}
@@ -62,29 +98,30 @@ static void
closure_setup(mrb_state *mrb, struct RProc *p)
{
mrb_callinfo *ci = mrb->c->ci;
- struct RProc *up = p->upper;
- struct REnv *e;
+ const struct RProc *up = p->upper;
+ struct REnv *e = NULL;
- if (ci->env) {
- e = ci->env;
+ if (ci && (e = mrb_vm_ci_env(ci)) != NULL) {
+ /* do nothing, because e is assigned already */
}
- else {
+ 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);
+ e = mrb_env_new(mrb, mrb->c, ci, up->body.irep->nlocals, ci->stack, tc);
+ ci->u.env = e;
+ if (MRB_PROC_ENV_P(up) && MRB_PROC_ENV(up)->cxt == NULL) {
+ e->mid = MRB_PROC_ENV(up)->mid;
}
}
- p->e.env = e;
- p->flags |= MRB_PROC_ENVSET;
- mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+ 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)
+mrb_closure_new(mrb_state *mrb, const mrb_irep *irep)
{
struct RProc *p = mrb_proc_new(mrb, irep);
@@ -97,7 +134,7 @@ 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 = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class);
p->body.func = func;
p->flags |= MRB_PROC_CFUNC_FL;
p->upper = 0;
@@ -113,11 +150,14 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const
struct REnv *e;
int i;
- p->e.env = e = env_new(mrb, argc);
+ p->e.env = e = mrb_env_new(mrb, mrb->c, mrb->c->ci, 0, NULL, NULL);
p->flags |= MRB_PROC_ENVSET;
mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
- MRB_ENV_UNSHARE_STACK(e);
+ MRB_ENV_CLOSE(e);
+
e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
+ MRB_ENV_SET_LEN(e, argc);
+
if (argv) {
for (i = 0; i < argc; ++i) {
e->stack[i] = argv[i];
@@ -140,19 +180,19 @@ mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
MRB_API mrb_value
mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
{
- struct RProc *p = mrb->c->ci->proc;
+ const 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.");
+ 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.");
+ 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)));
+ if (idx < 0 || MRB_ENV_LEN(e) <= idx) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %i (expected: 0 <= index < %i)",
+ idx, MRB_ENV_LEN(e));
}
return e->stack[idx];
@@ -168,7 +208,7 @@ mrb_proc_copy(struct RProc *a, struct RProc *b)
a->flags = b->flags;
a->body = b->body;
if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
- a->body.irep->refcnt++;
+ mrb_irep_incref(NULL, (mrb_irep*)a->body.irep);
}
a->upper = b->upper;
a->e.env = b->e.env;
@@ -182,17 +222,14 @@ mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
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));
+ /* Calling Proc.new without a block is not implemented yet */
+ mrb_get_args(mrb, "&!", &blk);
+ p = 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);
+ mrb_funcall_with_block(mrb, proc, MRB_SYM(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) {
+ mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].u.env) {
p->flags |= MRB_PROC_ORPHAN;
}
return proc;
@@ -201,46 +238,76 @@ mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
static mrb_value
mrb_proc_init_copy(mrb_state *mrb, mrb_value self)
{
- mrb_value proc;
+ mrb_value proc = mrb_get_arg1(mrb);
- mrb_get_args(mrb, "o", &proc);
- if (mrb_type(proc) != MRB_TT_PROC) {
+ if (!mrb_proc_p(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)
+/* 15.2.17.4.2 */
+static mrb_value
+proc_arity(mrb_state *mrb, mrb_value self)
{
- return MRB_PROC_CFUNC_P(p);
+ return mrb_int_value(mrb, mrb_proc_arity(mrb_proc_ptr(self)));
}
-/* 15.2.17.4.2 */
+/* 15.3.1.2.6 */
+/* 15.3.1.3.27 */
+/*
+ * call-seq:
+ * lambda { |...| block } -> a_proc
+ *
+ * Equivalent to <code>Proc.new</code>, except the resulting Proc objects
+ * check the number of parameters passed when called.
+ */
static mrb_value
-mrb_proc_arity(mrb_state *mrb, mrb_value self)
+proc_lambda(mrb_state *mrb, mrb_value self)
{
- struct RProc *p = mrb_proc_ptr(self);
- struct mrb_irep *irep;
- mrb_code *pc;
+ 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_proc_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
+ }
+ p = mrb_proc_ptr(blk);
+ if (!MRB_PROC_STRICT_P(p)) {
+ struct RProc *p2 = 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;
+}
+
+mrb_int
+mrb_proc_arity(const struct RProc *p)
+{
+ const mrb_irep *irep;
+ const 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);
+ return -1;
}
irep = p->body.irep;
if (!irep) {
- return mrb_fixnum_value(0);
+ return 0;
}
pc = irep->iseq;
/* arity is depend on OP_ENTER */
if (*pc != OP_ENTER) {
- return mrb_fixnum_value(0);
+ return 0;
}
aspec = PEEK_W(pc+1);
@@ -250,64 +317,132 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self)
pa = MRB_ASPEC_POST(aspec);
arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
- return mrb_fixnum_value(arity);
+ return arity;
}
-/* 15.3.1.2.6 */
-/* 15.3.1.3.27 */
-/*
- * call-seq:
- * lambda { |...| block } -> a_proc
- *
- * Equivalent to <code>Proc.new</code>, 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
+mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc)
{
- mrb_value blk;
- struct RProc *p;
+ const mrb_irep *irep;
+ mrb_value vars;
+ size_t i;
- 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 (proc == NULL || MRB_PROC_CFUNC_P(proc)) {
+ return mrb_ary_new(mrb);
}
- if (mrb_type(blk) != MRB_TT_PROC) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
+ vars = mrb_hash_new(mrb);
+ while (proc) {
+ if (MRB_PROC_CFUNC_P(proc)) break;
+ irep = proc->body.irep;
+ if (irep->lv) {
+ for (i = 0; i + 1 < irep->nlocals; ++i) {
+ if (irep->lv[i]) {
+ mrb_sym sym = irep->lv[i];
+ const char *name = mrb_sym_name(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_SCOPE_P(proc)) break;
+ proc = proc->upper;
}
- 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 mrb_hash_keys(mrb, vars);
+}
+
+const struct RProc *
+mrb_proc_get_caller(mrb_state *mrb, struct REnv **envp)
+{
+ struct mrb_context *c = mrb->c;
+ mrb_callinfo *ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase;
+ const struct RProc *proc = ci->proc;
+
+ if (!proc || MRB_PROC_CFUNC_P(proc)) {
+ if (envp) *envp = NULL;
}
- return blk;
+ else {
+ struct RClass *tc = MRB_PROC_TARGET_CLASS(proc);
+ struct REnv *e = mrb_vm_ci_env(ci);
+
+ if (e == NULL) {
+ int nstacks = proc->body.irep->nlocals;
+ e = mrb_env_new(mrb, c, ci, nstacks, ci->stack, tc);
+ ci->u.env = e;
+ }
+ else if (tc) {
+ e->c = tc;
+ mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
+ }
+ if (envp) *envp = e;
+ }
+
+ return proc;
+}
+
+#define IREP_LVAR_MERGE_DEFAULT 50
+#define IREP_LVAR_MERGE_MINIMUM 8
+#define IREP_LVAR_MERGE_MAXIMUM 240
+
+#ifdef MRB_IREP_LVAR_MERGE_LIMIT
+# define IREP_LVAR_MERGE_LIMIT \
+ ((MRB_IREP_LVAR_MERGE_LIMIT) < IREP_LVAR_MERGE_MINIMUM ? IREP_LVAR_MERGE_MINIMUM : \
+ (MRB_IREP_LVAR_MERGE_LIMIT) > IREP_LVAR_MERGE_MAXIMUM ? IREP_LVAR_MERGE_MAXIMUM : \
+ (MRB_IREP_LVAR_MERGE_LIMIT))
+#else
+# define IREP_LVAR_MERGE_LIMIT IREP_LVAR_MERGE_DEFAULT
+#endif
+
+void
+mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack)
+{
+ mrb_assert(!(irep->flags & MRB_IREP_NO_FREE));
+
+ if ((irep->nlocals + num) > IREP_LVAR_MERGE_LIMIT) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "too many local variables for binding (mruby limitation)");
+ }
+
+ if (!lv) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "unavailable local variable names");
+ }
+
+ irep->lv = (mrb_sym*)mrb_realloc(mrb, (mrb_sym*)irep->lv, sizeof(mrb_sym) * (irep->nlocals + num));
+ env->stack = (mrb_value*)mrb_realloc(mrb, env->stack, sizeof(mrb_value) * (irep->nlocals + 1 /* self */ + num));
+
+ mrb_sym *destlv = (mrb_sym*)irep->lv + irep->nlocals - 1 /* self */;
+ mrb_value *destst = env->stack + irep->nlocals;
+ memmove(destlv, lv, sizeof(mrb_sym) * num);
+ if (stack) {
+ memmove(destst, stack, sizeof(mrb_value) * num);
+ }
+ else {
+ for (int i = num; i > 0; i--, destst++) {
+ *destst = mrb_nil_value();
+ }
+ }
+ irep->nlocals += num;
+ irep->nregs = irep->nlocals;
+ MRB_ENV_SET_LEN(env, irep->nlocals);
}
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_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
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());
+ mrb_define_method(mrb, mrb->proc_class, "arity", 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_METHOD_FROM_PROC(m, &call_proc);
+ mrb_define_method_raw(mrb, mrb->proc_class, MRB_SYM(call), m);
+ mrb_define_method_raw(mrb, mrb->proc_class, MRB_OPSYM(aref), 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 */
+ mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.2.6 */
+ mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */
}
diff --git a/src/range.c b/src/range.c
index e45308d35..477baa5e8 100644
--- a/src/range.c
+++ b/src/range.c
@@ -9,55 +9,102 @@
#include <mruby/range.h>
#include <mruby/string.h>
#include <mruby/array.h>
+#include <mruby/presym.h>
-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;
-}
+#define RANGE_INITIALIZED_FLAG 1
+#define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_FLAG)
+#define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_FLAG)
static void
-range_check(mrb_state *mrb, mrb_value a, mrb_value b)
+r_check(mrb_state *mrb, mrb_value a, mrb_value b)
{
- mrb_value ans;
enum mrb_vtype ta;
enum mrb_vtype tb;
+ mrb_int n;
ta = mrb_type(a);
tb = mrb_type(b);
-#ifdef MRB_WITHOUT_FLOAT
- if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) {
+#ifdef MRB_NO_FLOAT
+ if (ta == MRB_TT_INTEGER && tb == MRB_TT_INTEGER ) return;
#else
- if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
- (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
-#endif
+ if ((ta == MRB_TT_INTEGER || ta == MRB_TT_FLOAT) &&
+ (tb == MRB_TT_INTEGER || tb == MRB_TT_FLOAT)) {
return;
}
+#endif
+
+ if (mrb_nil_p(a) || mrb_nil_p(b)) return;
- ans = mrb_funcall(mrb, a, "<=>", 1, b);
- if (mrb_nil_p(ans)) {
- /* can not be compared */
+ n = mrb_cmp(mrb, a, b);
+ if (n == -2) { /* 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)
+static mrb_bool
+r_le(mrb_state *mrb, mrb_value a, mrb_value b)
{
- struct RRange *r;
+ mrb_int n = mrb_cmp(mrb, a, b);
- range_check(mrb, beg, end);
- r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
+ if (n == 0 || n == -1) return TRUE;
+ return FALSE;
+}
+
+static mrb_bool
+r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ return mrb_cmp(mrb, a, b) == 1;
+}
+
+static mrb_bool
+r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ mrb_int n = mrb_cmp(mrb, a, b);
+
+ if (n == 0 || n == 1) return TRUE;
+ return FALSE;
+}
+
+static void
+range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
+{
+#ifndef MRB_RANGE_EMBED
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);
+#endif
+}
+
+static struct RRange *
+range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ r_check(mrb, beg, end);
+
+ if (r) {
+ if (RANGE_INITIALIZED_P(r)) {
+ /* Ranges are immutable, so that they should be initialized only once. */
+ mrb_name_error(mrb, MRB_SYM(initialize), "'initialize' called twice");
+ }
+ else {
+ range_ptr_alloc_edges(mrb, r);
+ }
+ }
+ else {
+ r = MRB_OBJ_ALLOC(mrb, MRB_TT_RANGE, mrb->range_class);
+ range_ptr_alloc_edges(mrb, r);
+ }
+
+ RANGE_BEG(r) = beg;
+ RANGE_END(r) = end;
+ RANGE_EXCL(r) = excl;
+ RANGE_INITIALIZED(r);
+
+ return r;
+}
+
+static void
+range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ range_ptr_init(mrb, r, beg, end, excl);
+ mrb_write_barrier(mrb, (struct RBasic*)r);
}
/*
@@ -67,12 +114,10 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
*
* Returns the first object in <i>rng</i>.
*/
-mrb_value
-mrb_range_beg(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_beg(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(mrb, range);
-
- return r->edges->beg;
+ return mrb_range_beg(mrb, range);
}
/*
@@ -85,13 +130,10 @@ mrb_range_beg(mrb_state *mrb, mrb_value range)
* (1..10).end #=> 10
* (1...10).end #=> 10
*/
-
-mrb_value
-mrb_range_end(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_end(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(mrb, range);
-
- return r->edges->end;
+ return mrb_range_end(mrb, range);
}
/*
@@ -100,28 +142,12 @@ mrb_range_end(mrb_state *mrb, mrb_value range)
*
* Returns <code>true</code> if <i>range</i> excludes its end value.
*/
-mrb_value
-mrb_range_excl(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_excl(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(mrb, range);
-
- return mrb_bool_value(r->excl);
+ return mrb_bool_value(mrb_range_excl_p(mrb, range));
}
-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
@@ -130,25 +156,18 @@ range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bo
* parameter is omitted or is <code>false</code>, the <i>range</i> will include
* the end object; otherwise, it will be excluded.
*/
-
-mrb_value
-mrb_range_initialize(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_initialize(mrb_state *mrb, mrb_value range)
{
mrb_value beg, end;
- mrb_bool exclusive;
- mrb_int n;
+ mrb_bool exclusive = FALSE;
- 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);
+ mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
+ range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
+ mrb_obj_freeze(mrb, range);
return range;
}
+
/*
* call-seq:
* range == obj => true or false
@@ -161,17 +180,14 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
* (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)
+static mrb_value
+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);
+ mrb_value obj = mrb_get_arg1(mrb);
+ mrb_bool v1, v2;
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? */
@@ -180,108 +196,45 @@ mrb_range_eq(mrb_state *mrb, mrb_value range)
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) {
+ v1 = mrb_equal(mrb, RANGE_BEG(rr), RANGE_BEG(ro));
+ v2 = mrb_equal(mrb, RANGE_END(rr), RANGE_END(ro));
+ if (!v1 || !v2 || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
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)
+static mrb_value
+range_include(mrb_state *mrb, mrb_value range)
{
- mrb_value val;
+ mrb_value val = mrb_get_arg1(mrb);
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;
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ if (mrb_nil_p(beg)) {
+ if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
+ : r_ge(mrb, end, val)) { /* end >= val */
+ return mrb_true_value();
+ }
}
-
- if (trunc) {
- if (beg > len) return 2;
- if (end > len) end = len;
+ else if (r_le(mrb, beg, val)) { /* beg <= val */
+ if (mrb_nil_p(end)) {
+ return mrb_true_value();
+ }
+ if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
+ : r_ge(mrb, end, val)) { /* end >= val */
+ return mrb_true_value();
+ }
}
-
- 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;
+ return mrb_false_value();
}
/* 15.2.14.4.12(x) */
@@ -291,17 +244,16 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
*
* 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_obj_as_string(mrb, RANGE_BEG(r));
+ str2 = mrb_obj_as_string(mrb, RANGE_END(r));
str = mrb_str_dup(mrb, str);
- mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
+ mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
mrb_str_cat_str(mrb, str, str2);
return str;
@@ -316,18 +268,24 @@ range_to_s(mrb_state *mrb, mrb_value range)
* <code>inspect</code> to convert the start and end
* objects).
*/
-
static mrb_value
range_inspect(mrb_state *mrb, mrb_value range)
{
- mrb_value str, str2;
+ mrb_value str;
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);
+ if (!mrb_nil_p(RANGE_BEG(r))) {
+ str = mrb_inspect(mrb, RANGE_BEG(r));
+ str = mrb_str_dup(mrb, str);
+ mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
+ }
+ else {
+ str = mrb_str_new(mrb, "...", RANGE_EXCL(r) ? 3 : 2);
+ }
+ if (!mrb_nil_p(RANGE_END(r))) {
+ mrb_value str2 = mrb_inspect(mrb, RANGE_END(r));
+ mrb_str_cat_str(mrb, str, str2);
+ }
return str;
}
@@ -344,28 +302,22 @@ range_inspect(mrb_state *mrb, mrb_value range)
* (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;
+ mrb_value obj = mrb_get_arg1(mrb);
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();
+ if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value();
+ if (!mrb_range_p(obj)) 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)) {
+ if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
+ !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
+ (RANGE_EXCL(r) != RANGE_EXCL(o))) {
return mrb_false_value();
}
return mrb_true_value();
@@ -375,22 +327,71 @@ range_eql(mrb_state *mrb, mrb_value range)
static mrb_value
range_initialize_copy(mrb_state *mrb, mrb_value copy)
{
- mrb_value src;
+ mrb_value src = mrb_get_arg1(mrb);
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);
+ range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
+ mrb_obj_freeze(mrb, copy);
return copy;
}
+static mrb_value
+range_num_to_a(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value beg = RANGE_BEG(r);
+ mrb_value end = RANGE_END(r);
+ mrb_value ary;
+
+ if (mrb_nil_p(end)) {
+ mrb->c->ci->mid = 0;
+ mrb_raise(mrb, E_RANGE_ERROR, "cannot convert endless range to an array");
+ }
+ if (mrb_integer_p(beg)) {
+ if (mrb_integer_p(end)) {
+ mrb_int a = mrb_integer(beg);
+ mrb_int b = mrb_integer(end);
+ mrb_int len = b - a;
+
+ if (!RANGE_EXCL(r)) len++;
+ ary = mrb_ary_new_capa(mrb, len);
+ for (mrb_int i=0; i<len; i++) {
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, a+i));
+ }
+ return ary;
+ }
+#ifndef MRB_NO_FLOAT
+ if (mrb_float_p(end)) {
+ mrb_float a = (mrb_float)mrb_integer(beg);
+ mrb_float b = mrb_float(end);
+
+ ary = mrb_ary_new_capa(mrb, (mrb_int)(b - a) + 1);
+ if (RANGE_EXCL(r)) {
+ while (a < b) {
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, (mrb_int)a));
+ a += 1.0;
+ }
+ }
+ else {
+ while (a <= b) {
+ mrb_ary_push(mrb, ary, mrb_int_value(mrb, (mrb_int)a));
+ a += 1.0;
+ }
+ }
+ return ary;
+ }
+#endif
+ }
+ return mrb_nil_value();
+}
+
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))
{
@@ -399,10 +400,10 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
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])));
+ if (mrb_integer_p(argv[i])) {
+ mrb_ary_push(mrb, result, func(mrb, obj, mrb_integer(argv[i])));
}
- else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) {
+ else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == MRB_RANGE_OK) {
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));
@@ -413,7 +414,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
}
}
else {
- mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]);
+ mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %v", argv[i]);
}
}
@@ -421,6 +422,68 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
}
void
+mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
+{
+ if (RANGE_INITIALIZED_P(r)) {
+ mrb_gc_mark_value(mrb, RANGE_BEG(r));
+ mrb_gc_mark_value(mrb, RANGE_END(r));
+ }
+}
+
+MRB_API struct RRange*
+mrb_range_ptr(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_raw_ptr(range);
+
+ /* check for if #initialize_copy was removed [#3320] */
+ if (!RANGE_INITIALIZED_P(r)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
+ }
+ return r;
+}
+
+MRB_API mrb_value
+mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
+ return mrb_range_value(r);
+}
+
+MRB_API enum mrb_range_beg_len
+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;
+ mrb_bool excl;
+ struct RRange *r;
+
+ if (!mrb_range_p(range)) return MRB_RANGE_TYPE_MISMATCH;
+ r = mrb_range_ptr(mrb, range);
+
+ beg = mrb_nil_p(RANGE_BEG(r)) ? 0 : mrb_as_int(mrb, RANGE_BEG(r));
+ end = mrb_nil_p(RANGE_END(r)) ? -1 : mrb_as_int(mrb, RANGE_END(r));
+ excl = mrb_nil_p(RANGE_END(r)) ? 0 : RANGE_EXCL(r);
+
+ if (beg < 0) {
+ beg += len;
+ if (beg < 0) return MRB_RANGE_OUT;
+ }
+
+ if (trunc) {
+ if (beg > len) return MRB_RANGE_OUT;
+ if (end > len) end = len;
+ }
+
+ if (end < 0) end += len;
+ if (!excl && (!trunc || end < len)) end++; /* include end point */
+ len = end - beg;
+ if (len < 0) len = 0;
+
+ *begp = beg;
+ *lenp = len;
+ return MRB_RANGE_OK;
+}
+
+void
mrb_init_range(mrb_state *mrb)
{
struct RClass *r;
@@ -429,19 +492,19 @@ mrb_init_range(mrb_state *mrb)
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, "begin", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
+ mrb_define_method(mrb, r, "end", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
+ mrb_define_method(mrb, r, "==", range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
+ mrb_define_method(mrb, r, "===", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
+ mrb_define_method(mrb, r, "exclude_end?", range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
+ mrb_define_method(mrb, r, "first", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
+ mrb_define_method(mrb, r, "include?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
+ mrb_define_method(mrb, r, "initialize", range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
+ mrb_define_method(mrb, r, "last", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
+ mrb_define_method(mrb, r, "member?", 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) */
+ mrb_define_method(mrb, r, "__num_to_a", range_num_to_a, MRB_ARGS_NONE());
}
diff --git a/src/readflt.c b/src/readflt.c
new file mode 100644
index 000000000..19a8e8dc6
--- /dev/null
+++ b/src/readflt.c
@@ -0,0 +1,120 @@
+#include <mruby.h>
+
+#ifndef MRB_NO_FLOAT
+/*
+ * strtod implementation.
+ * author: Yasuhiro Matsumoto (@mattn)
+ * license: public domain
+ */
+
+/*
+The original code can be found in https://github.com/mattn/strtod
+
+I modified the routine for mruby:
+
+ * renamed the function `vim_strtod` -> `mrb_float_read`
+ * simplified the code
+
+My modifications in this file are also placed in the public domain.
+
+Matz (Yukihiro Matsumoto)
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+
+MRB_API double
+mrb_float_read(const char *str, char **end)
+{
+ double d = 0.0;
+ int sign;
+ int n = 0;
+ const char *p, *a;
+
+ a = p = str;
+ while (ISSPACE(*p))
+ ++p;
+
+ /* decimal part */
+ sign = 1;
+ if (*p == '-') {
+ sign = -1;
+ ++p;
+ } else if (*p == '+')
+ ++p;
+ if (ISDIGIT(*p)) {
+ d = (double)(*p++ - '0');
+ while (*p && ISDIGIT(*p)) {
+ d = d * 10.0 + (double)(*p - '0');
+ ++p;
+ ++n;
+ }
+ a = p;
+ } else if (*p != '.')
+ goto done;
+ d *= sign;
+
+ /* fraction part */
+ if (*p == '.') {
+ double f = 0.0;
+ double base = 0.1;
+ ++p;
+
+ if (ISDIGIT(*p))
+ {
+ while (*p && ISDIGIT(*p)) {
+ f += base * (*p - '0') ;
+ base /= 10.0;
+ ++p;
+ ++n;
+ }
+ }
+ d += f * sign;
+ a = p;
+ }
+
+ /* exponential part */
+ if ((*p == 'E') || (*p == 'e')) {
+ int e = 0;
+ ++p;
+
+ sign = 1;
+ if (*p == '-') {
+ sign = -1;
+ ++p;
+ } else if (*p == '+')
+ ++p;
+
+ if (ISDIGIT(*p)) {
+ while (*p == '0')
+ ++p;
+ if (*p == '\0') --p;
+ e = (int)(*p++ - '0');
+ for (; *p && ISDIGIT(*p); p++) {
+ if (e < 10000)
+ e = e * 10 + (*p - '0');
+ }
+ e *= sign;
+ }
+ else if (!ISDIGIT(*(a-1))) {
+ a = str;
+ goto done;
+ }
+ else if (*p == 0)
+ goto done;
+ d *= pow(10.0, (double) e);
+ a = p;
+ }
+ else if (p > str && !ISDIGIT(*(p-1))) {
+ a = str;
+ goto done;
+ }
+
+done:
+ if (end)
+ *end = (char*)a;
+ return d;
+}
+#endif
diff --git a/src/readint.c b/src/readint.c
new file mode 100644
index 000000000..5fae222c2
--- /dev/null
+++ b/src/readint.c
@@ -0,0 +1,30 @@
+#include <mruby.h>
+#include <mruby/numeric.h>
+#include <errno.h>
+
+/* mrb_int_read(): read mrb_int from a string (base 10 only) */
+/* const char *p - string to read */
+/* const char *e - end of string */
+/* char **endp - end of parsed integer */
+
+/* if integer overflows, errno will be set to ERANGE */
+/* also endp will be set to NULL on overflow */
+MRB_API mrb_int
+mrb_int_read(const char *p, const char *e, char **endp)
+{
+ mrb_int n = 0;
+ int ch;
+
+ while ((e == NULL || p < e) && ISDIGIT(*p)) {
+ ch = *p - '0';
+ if (mrb_int_mul_overflow(n, 10, &n) ||
+ mrb_int_add_overflow(n, ch, &n)) {
+ if (endp) *endp = NULL;
+ errno = ERANGE;
+ return MRB_INT_MAX;
+ }
+ p++;
+ }
+ if (endp) *endp = (char*)p;
+ return n;
+}
diff --git a/src/state.c b/src/state.c
index 18d104555..4dafc964a 100644
--- a/src/state.c
+++ b/src/state.c
@@ -19,13 +19,28 @@ void mrb_init_mrbgems(mrb_state*);
void mrb_gc_init(mrb_state*, mrb_gc *gc);
void mrb_gc_destroy(mrb_state*, mrb_gc *gc);
+int mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque);
+
+static void
+init_gc_and_core(mrb_state *mrb, void *opaque)
+{
+ static const struct mrb_context mrb_context_zero = { 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);
+}
+
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;
+ if (f == NULL) f = mrb_default_allocf;
mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
if (mrb == NULL) return NULL;
@@ -34,12 +49,10 @@ mrb_open_core(mrb_allocf f, void *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);
+ if (mrb_core_init_protect(mrb, init_gc_and_core, NULL)) {
+ mrb_close(mrb);
+ return NULL;
+ }
return mrb;
}
@@ -56,38 +69,6 @@ mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)
}
}
-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)
{
@@ -96,6 +77,14 @@ mrb_open(void)
return mrb;
}
+#ifndef MRB_NO_GEMS
+static void
+init_mrbgems(mrb_state *mrb, void *opaque)
+{
+ mrb_init_mrbgems(mrb);
+}
+#endif
+
MRB_API mrb_state*
mrb_open_allocf(mrb_allocf f, void *ud)
{
@@ -105,8 +94,11 @@ mrb_open_allocf(mrb_allocf f, void *ud)
return NULL;
}
-#ifndef DISABLE_GEMS
- mrb_init_mrbgems(mrb);
+#ifndef MRB_NO_GEMS
+ if (mrb_core_init_protect(mrb, init_mrbgems, NULL)) {
+ mrb_close(mrb);
+ return NULL;
+ }
mrb_gc_arena_restore(mrb, 0);
#endif
return mrb;
@@ -117,12 +109,20 @@ void mrb_free_symtbl(mrb_state *mrb);
void
mrb_irep_incref(mrb_state *mrb, mrb_irep *irep)
{
+ if (irep->flags & MRB_IREP_NO_FREE) return;
+ if (irep->refcnt == UINT16_MAX) {
+ mrb_garbage_collect(mrb);
+ if (irep->refcnt == UINT16_MAX) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "too many irep references");
+ }
+ }
irep->refcnt++;
}
void
mrb_irep_decref(mrb_state *mrb, mrb_irep *irep)
{
+ if (irep->flags & MRB_IREP_NO_FREE) return;
irep->refcnt--;
if (irep->refcnt == 0) {
mrb_irep_free(mrb, irep);
@@ -132,12 +132,14 @@ mrb_irep_decref(mrb_state *mrb, mrb_irep *irep)
void
mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep)
{
- mrb_irep *tmp;
+ mrb_irep **reps;
int i;
+ if (irep->flags & MRB_IREP_NO_FREE) return;
+ reps = (mrb_irep**)irep->reps;
for (i=0; i<irep->rlen; i++) {
- tmp = irep->reps[i];
- irep->reps[i] = NULL;
+ mrb_irep *tmp = reps[i];
+ reps[i] = NULL;
if (tmp) mrb_irep_decref(mrb, tmp);
}
}
@@ -147,120 +149,53 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
{
int i;
+ if (irep->flags & MRB_IREP_NO_FREE) return;
if (!(irep->flags & MRB_ISEQ_NO_FREE))
- mrb_free(mrb, irep->iseq);
- if (irep->pool) for (i=0; i<irep->plen; 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]));
+ mrb_free(mrb, (void*)irep->iseq);
+ if (irep->pool) {
+ for (i=0; i<irep->plen; i++) {
+ if ((irep->pool[i].tt & 3) == IREP_TT_STR ||
+ irep->pool[i].tt == IREP_TT_BIGINT) {
+ mrb_free(mrb, (void*)irep->pool[i].u.str);
+ }
}
-#endif
- }
- mrb_free(mrb, irep->pool);
- mrb_free(mrb, irep->syms);
- for (i=0; i<irep->rlen; i++) {
- if (irep->reps[i])
- mrb_irep_decref(mrb, irep->reps[i]);
+ mrb_free(mrb, (void*)irep->pool);
}
- mrb_free(mrb, irep->reps);
- mrb_free(mrb, irep->lv);
- if (irep->own_filename) {
- mrb_free(mrb, (void *)irep->filename);
+ mrb_free(mrb, (void*)irep->syms);
+ if (irep->reps) {
+ for (i=0; i<irep->rlen; i++) {
+ if (irep->reps[i])
+ mrb_irep_decref(mrb, (mrb_irep*)irep->reps[i]);
+ }
+ mrb_free(mrb, (void*)irep->reps);
}
- mrb_free(mrb, irep->lines);
+ mrb_free(mrb, (void*)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
+void mrb_protect_atexit(mrb_state *mrb);
+
+ 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
- }
+ mrb_protect_atexit(mrb);
/* free */
- mrb_gc_free_gv(mrb);
+ mrb_gc_destroy(mrb, &mrb->gc);
mrb_free_context(mrb, mrb->root_c);
+ mrb_gc_free_gv(mrb);
mrb_free_symtbl(mrb);
- mrb_alloca_free(mrb);
- mrb_gc_destroy(mrb, &mrb->gc);
mrb_free(mrb, mrb);
}
@@ -273,7 +208,6 @@ mrb_add_irep(mrb_state *mrb)
irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
*irep = mrb_irep_zero;
irep->refcnt = 1;
- irep->own_filename = FALSE;
return irep;
}
diff --git a/src/string.c b/src/string.c
index b7abfb762..03d9c8a9c 100644
--- a/src/string.c
+++ b/src/string.c
@@ -8,8 +8,9 @@
# define _CRT_NONSTDC_NO_DEPRECATE
#endif
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
#include <float.h>
+#include <math.h>
#endif
#include <limits.h>
#include <stddef.h>
@@ -20,80 +21,124 @@
#include <mruby/class.h>
#include <mruby/range.h>
#include <mruby/string.h>
-#include <mruby/re.h>
+#include <mruby/numeric.h>
+#include <mruby/presym.h>
typedef struct mrb_shared_string {
- mrb_bool nofree : 1;
int refcnt;
+ mrb_ssize capa;
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))
+#define mrb_obj_alloc_string(mrb) 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)
+str_init_normal_capa(mrb_state *mrb, struct RString *s,
+ const char *p, size_t len, size_t capa)
+{
+ char *dst = (char *)mrb_malloc(mrb, capa + 1);
+ if (p) memcpy(dst, p, len);
+ dst[len] = '\0';
+ s->as.heap.ptr = dst;
+ s->as.heap.len = (mrb_ssize)len;
+ s->as.heap.aux.capa = (mrb_ssize)capa;
+ RSTR_UNSET_TYPE_FLAG(s);
+ return s;
+}
+
+static struct RString*
+str_init_normal(mrb_state *mrb, struct RString *s, const char *p, size_t len)
{
- struct RString *s;
+ return str_init_normal_capa(mrb, s, p, len, len);
+}
- 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 */
+static struct RString*
+str_init_embed(struct RString *s, const char *p, size_t len)
+{
+ if (p) memcpy(RSTR_EMBED_PTR(s), p, len);
+ RSTR_EMBED_PTR(s)[len] = '\0';
+ RSTR_SET_TYPE_FLAG(s, EMBED);
+ RSTR_SET_EMBED_LEN(s, len);
+ return s;
+}
+
+static struct RString*
+str_init_nofree(struct RString *s, const char *p, size_t len)
+{
s->as.heap.ptr = (char *)p;
- s->flags = MRB_STR_NOFREE;
+ s->as.heap.len = (mrb_ssize)len;
+ s->as.heap.aux.capa = 0; /* nofree */
+ RSTR_SET_TYPE_FLAG(s, NOFREE);
+ return s;
+}
+static struct RString*
+str_init_shared(mrb_state *mrb, const struct RString *orig, struct RString *s, mrb_shared_string *shared)
+{
+ if (shared) {
+ shared->refcnt++;
+ }
+ else {
+ shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
+ shared->refcnt = 1;
+ shared->ptr = orig->as.heap.ptr;
+ shared->capa = orig->as.heap.aux.capa;
+ }
+ s->as.heap.ptr = orig->as.heap.ptr;
+ s->as.heap.len = orig->as.heap.len;
+ s->as.heap.aux.shared = shared;
+ RSTR_SET_TYPE_FLAG(s, SHARED);
return s;
}
static struct RString*
-str_new(mrb_state *mrb, const char *p, size_t len)
+str_init_fshared(const struct RString *orig, struct RString *s, struct RString *fshared)
{
- struct RString *s;
+ s->as.heap.ptr = orig->as.heap.ptr;
+ s->as.heap.len = orig->as.heap.len;
+ s->as.heap.aux.fshared = fshared;
+ RSTR_SET_TYPE_FLAG(s, FSHARED);
+ return 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);
- }
+static struct RString*
+str_init_modifiable(mrb_state *mrb, struct RString *s, const char *p, size_t len)
+{
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(s, 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);
- }
+ return str_init_normal(mrb, s, p, len);
}
- RSTR_PTR(s)[len] = '\0';
- return s;
}
-static inline void
-str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj)
+static struct RString*
+str_new_static(mrb_state *mrb, const char *p, size_t len)
{
- s->c = mrb_str_ptr(obj)->c;
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(mrb_obj_alloc_string(mrb), p, len);
+ }
+ if (len >= MRB_SSIZE_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
+ return str_init_nofree(mrb_obj_alloc_string(mrb), p, len);
}
-static mrb_value
-mrb_str_new_empty(mrb_state *mrb, mrb_value str)
+static struct RString*
+str_new(mrb_state *mrb, const char *p, size_t len)
{
- struct RString *s = str_new(mrb, 0, 0);
-
- str_with_class(mrb, s, str);
- return mrb_obj_value(s);
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(mrb_obj_alloc_string(mrb), p, len);
+ }
+ if (len >= MRB_SSIZE_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
+ if (p && mrb_ro_data_p(p)) {
+ return str_init_nofree(mrb_obj_alloc_string(mrb), p, len);
+ }
+ return str_init_normal(mrb, mrb_obj_alloc_string(mrb), p, len);
}
MRB_API mrb_value
@@ -101,52 +146,35 @@ mrb_str_new_capa(mrb_state *mrb, size_t capa)
{
struct RString *s;
- s = mrb_obj_alloc_string(mrb);
-
- if (capa >= MRB_INT_MAX) {
+ if (RSTR_EMBEDDABLE_P(capa)) {
+ s = str_init_embed(mrb_obj_alloc_string(mrb), NULL, 0);
+ }
+ else if (capa >= MRB_SSIZE_MAX) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big");
+ /* not reached */
+ s = NULL;
+ }
+ else {
+ s = str_init_normal_capa(mrb, mrb_obj_alloc_string(mrb), NULL, 0, capa);
}
- 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);
+#if SIZE_MAX > MRB_SSIZE_MAX
+ mrb_assert(capacity < MRB_SSIZE_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;
+ if (!RSTR_EMBEDDABLE_P(capacity)) {
+ str_init_normal_capa(mrb, s, RSTR_EMBED_PTR(s), RSTR_EMBED_LEN(s), capacity);
}
}
else {
s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
- s->as.heap.aux.capa = (mrb_int)capacity;
+ s->as.heap.aux.capa = (mrb_ssize)capacity;
}
}
@@ -156,13 +184,6 @@ 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 <i>str</i>.
- */
-
MRB_API mrb_value
mrb_str_new_cstr(mrb_state *mrb, const char *p)
{
@@ -193,13 +214,42 @@ 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->ptr);
mrb_free(mrb, shared);
}
}
+static void
+str_modify_keep_ascii(mrb_state *mrb, struct RString *s)
+{
+ if (RSTR_SHARED_P(s)) {
+ mrb_shared_string *shared = s->as.heap.aux.shared;
+
+ if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
+ s->as.heap.aux.capa = shared->capa;
+ s->as.heap.ptr[s->as.heap.len] = '\0';
+ RSTR_UNSET_SHARED_FLAG(s);
+ mrb_free(mrb, shared);
+ }
+ else {
+ str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len);
+ str_decref(mrb, shared);
+ }
+ }
+ else if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) {
+ str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len);
+ }
+}
+
+static void
+check_null_byte(mrb_state *mrb, mrb_value str)
+{
+ mrb_to_str(mrb, str);
+ if (memchr(RSTRING_PTR(str), '\0', RSTRING_LEN(str))) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+ }
+}
+
void
mrb_gc_free_str(mrb_state *mrb, struct RString *str)
{
@@ -220,104 +270,254 @@ 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,
- 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,
+ 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,
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)
+#define utf8_islead(c) ((unsigned char)((c)&0xc0) != 0x80)
+
+mrb_int
+mrb_utf8len(const char* p, const char* e)
{
mrb_int len;
mrb_int i;
+ if ((unsigned char)*p < 0x80) return 1;
len = utf8len_codepage[(unsigned char)*p];
- if (p + len > e) return 1;
+ if (len == 1) return 1;
+ if (len > e - p) return 1;
for (i = 1; i < len; ++i)
- if ((p[i] & 0xc0) != 0x80)
+ if (utf8_islead(p[i]))
return 1;
return len;
}
-static mrb_int
-utf8_strlen(mrb_value str, mrb_int len)
+mrb_int
+mrb_utf8_strlen(const char *str, mrb_int byte_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);
+ mrb_int len = 0;
+ const char *p = str;
+ const char *e = p + byte_len;
+
+ while (p < e) {
+ p += mrb_utf8len(p, e);
+ len++;
}
- e += len < 0 ? RSTRING_LEN(str) : len;
- while (p<e) {
- p += utf8len(p, e);
- total++;
+ return len;
+}
+
+static mrb_int
+utf8_strlen(mrb_value str)
+{
+ struct RString *s = mrb_str_ptr(str);
+ mrb_int byte_len = RSTR_LEN(s);
+
+ if (RSTR_ASCII_P(s)) {
+ return byte_len;
}
- if (RSTRING_LEN(str) == total) {
- RSTRING(str)->flags |= MRB_STR_NO_UTF;
+ else {
+ mrb_int utf8_len = mrb_utf8_strlen(RSTR_PTR(s), byte_len);
+ if (byte_len == utf8_len) RSTR_SET_ASCII_FLAG(s);
+ return utf8_len;
}
- return total;
}
-#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1)
+#define RSTRING_CHAR_LEN(s) utf8_strlen(s)
/* 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);
+ if (RSTR_ASCII_P(mrb_str_ptr(s))) {
+ return idx;
+ }
+ else {
+ mrb_int i, b, n;
+ const char *p = RSTRING_PTR(s) + off;
+ const char *e = RSTRING_END(s);
- for (b=i=0; p<e && i<idx; i++) {
- n = utf8len(p, e);
- b += n;
- p += n;
+ for (b=i=0; p<e && i<idx; i++) {
+ n = mrb_utf8len(p, e);
+ b += n;
+ p += n;
+ }
+ return b;
}
- return b;
}
/* map byte offset to character index */
static mrb_int
-bytes2chars(char *p, mrb_int bi)
+bytes2chars(char *p, mrb_int len, mrb_int bi)
{
- mrb_int i, b, n;
+ const char *e = p + (size_t)len;
+ const char *pivot = p + bi;
+ mrb_int i;
- for (b=i=0; b<bi; i++) {
- n = utf8len_codepage[(unsigned char)*p];
- b += n;
- p += n;
+ for (i = 0; p < pivot; i ++) {
+ p += mrb_utf8len(p, e);
}
- if (b != bi) return -1;
+ if (p != pivot) return -1;
return i;
}
+static const char *
+char_adjust(const char *beg, const char *end, const char *ptr)
+{
+ if ((ptr > beg || ptr < end) && (*ptr & 0xc0) == 0x80) {
+ const int utf8_adjust_max = 3;
+ const char *p;
+
+ if (ptr - beg > utf8_adjust_max) {
+ beg = ptr - utf8_adjust_max;
+ }
+
+ p = ptr;
+ while (p > beg) {
+ p --;
+ if ((*p & 0xc0) != 0x80) {
+ int clen = mrb_utf8len(p, end);
+ if (clen > ptr - p) return p;
+ break;
+ }
+ }
+ }
+
+ return ptr;
+}
+
+static const char *
+char_backtrack(const char *ptr, const char *end)
+{
+ if (ptr < end) {
+ const int utf8_bytelen_max = 4;
+ const char *p;
+
+ if (end - ptr > utf8_bytelen_max) {
+ ptr = end - utf8_bytelen_max;
+ }
+
+ p = end;
+ while (p > ptr) {
+ p --;
+ if ((*p & 0xc0) != 0x80) {
+ int clen = utf8len_codepage[(unsigned char)*p];
+ if (clen == end - p) { return p; }
+ break;
+ }
+ }
+ }
+
+ return end - 1;
+}
+
+static mrb_int
+str_index_str_by_char_search(mrb_state *mrb, const char *p, const char *pend, const char *s, const mrb_int slen, mrb_int off)
+{
+ /* Based on Quick Search algorithm (Boyer-Moore-Horspool algorithm) */
+
+ ptrdiff_t qstable[1 << CHAR_BIT];
+
+ /* Preprocessing */
+ {
+ mrb_int i;
+
+ for (i = 0; i < 1 << CHAR_BIT; i ++) {
+ qstable[i] = slen;
+ }
+ for (i = 0; i < slen; i ++) {
+ qstable[(unsigned char)s[i]] = slen - (i + 1);
+ }
+ }
+
+ /* Searching */
+ while (p < pend && pend - p >= slen) {
+ const char *pivot;
+
+ if (memcmp(p, s, slen) == 0) {
+ return off;
+ }
+
+ pivot = p + qstable[(unsigned char)p[slen - 1]];
+ if (pivot >= pend || pivot < p /* overflowed */) { return -1; }
+
+ do {
+ p += mrb_utf8len(p, pend);
+ off ++;
+ } while (p < pivot);
+ }
+
+ return -1;
+}
+
+static mrb_int
+str_index_str_by_char(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
+{
+ const char *p = RSTRING_PTR(str);
+ const char *pend = p + RSTRING_LEN(str);
+ const char *s = RSTRING_PTR(sub);
+ const mrb_int slen = RSTRING_LEN(sub);
+ mrb_int off = pos;
+
+ for (; pos > 0; pos --) {
+ if (pend - p < 1) { return -1; }
+ p += mrb_utf8len(p, pend);
+ }
+
+ if (slen < 1) { return off; }
+
+ return str_index_str_by_char_search(mrb, p, pend, s, slen, off);
+}
+
#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value();
#else
#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
#define chars2bytes(p, off, ci) (ci)
-#define bytes2chars(p, bi) (bi)
+#define bytes2chars(p, end, bi) (bi)
+#define char_adjust(beg, end, ptr) (ptr)
+#define char_backtrack(ptr, end) ((end) - 1)
#define BYTES_ALIGN_CHECK(pos)
+#define str_index_str_by_char(mrb, str, sub, pos) str_index_str(mrb, str, sub, pos)
+#endif
+
+#ifndef MRB_QS_SHORT_STRING_LENGTH
+#define MRB_QS_SHORT_STRING_LENGTH 2048
#endif
static inline mrb_int
mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n)
{
- const unsigned char *x = xs, *xe = xs + m;
- const unsigned char *y = ys;
- int i;
- ptrdiff_t qstable[256];
+ if (n + m < MRB_QS_SHORT_STRING_LENGTH) {
+ const unsigned char *y = ys;
+ const unsigned char *ye = ys+n-m+1;
- /* Preprocessing */
- for (i = 0; i < 256; ++i)
- qstable[i] = m + 1;
- for (; x < xe; ++x)
- qstable[*x] = xe - x;
- /* Searching */
- for (; y + m <= ys + n; y += *(qstable + y[m])) {
- if (*xs == *y && memcmp(xs, y, m) == 0)
- return (mrb_int)(y - ys);
+ for (;;) {
+ y = (const unsigned char*)memchr(y, xs[0], (size_t)(ye-y));
+ if (y == NULL) return -1;
+ if (memcmp(xs, y, m) == 0) {
+ return (mrb_int)(y - ys);
+ }
+ y++;
+ }
+ return -1;
+ }
+ else {
+ const unsigned char *x = xs, *xe = xs + m;
+ const unsigned char *y = ys;
+ int i;
+ ptrdiff_t qstable[256];
+
+ /* Preprocessing */
+ for (i = 0; i < 256; ++i)
+ qstable[i] = m + 1;
+ for (; x < xe; ++x)
+ qstable[*x] = xe - x;
+ /* Searching */
+ for (; y + m <= ys + n; y += *(qstable + y[m])) {
+ if (*xs == *y && memcmp(xs, y, m) == 0)
+ return (mrb_int)(y - ys);
+ }
+ return -1;
}
- return -1;
}
static mrb_int
@@ -344,113 +544,87 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
}
static void
-str_make_shared(mrb_state *mrb, struct RString *orig, struct RString *s)
+str_share(mrb_state *mrb, struct RString *orig, struct RString *s)
{
- mrb_shared_string *shared;
- mrb_int len = RSTR_LEN(orig);
+ size_t len = (size_t)orig->as.heap.len;
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);
+ if (RSTR_NOFREE_P(orig)) {
+ str_init_nofree(s, orig->as.heap.ptr, len);
+ }
+ else if (RSTR_SHARED_P(orig)) {
+ str_init_shared(mrb, orig, s, orig->as.heap.aux.shared);
}
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);
+ str_init_fshared(orig, s, orig->as.heap.aux.fshared);
}
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;
+ if (orig->as.heap.aux.capa > orig->as.heap.len) {
+ orig->as.heap.ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1);
+ orig->as.heap.aux.capa = (mrb_ssize)len;
}
- 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);
+ str_init_shared(mrb, orig, s, NULL);
+ str_init_shared(mrb, orig, orig, s->as.heap.aux.shared);
}
}
-static mrb_value
-byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+mrb_value
+mrb_str_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);
+ s = mrb_obj_alloc_string(mrb);
+ if (RSTR_EMBEDDABLE_P(len)) {
+ str_init_embed(s, 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;
+ str_share(mrb, orig, s);
+ s->as.heap.ptr += (mrb_ssize)beg;
+ s->as.heap.len = (mrb_ssize)len;
}
+ RSTR_COPY_ASCII_FLAG(s, orig);
return mrb_obj_value(s);
}
+
+static void
+str_range_to_bytes(mrb_value str, mrb_int *pos, mrb_int *len)
+{
+ *pos = chars2bytes(str, 0, *pos);
+ *len = chars2bytes(str, *pos, *len);
+}
#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);
+ str_range_to_bytes(str, &beg, &len);
+ return mrb_str_byte_subseq(mrb, str, beg, len);
}
#else
-#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len)
+#define str_subseq(mrb, str, beg, len) mrb_str_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_bool
+mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp)
{
- 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 (str_len < *begp || *lenp < 0) return FALSE;
+ if (*begp < 0) {
+ *begp += str_len;
+ if (*begp < 0) return FALSE;
}
- 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;
+ if (*lenp > str_len - *begp)
+ *lenp = str_len - *begp;
+ if (*lenp <= 0) {
+ *lenp = 0;
}
- return str_subseq(mrb, str, beg, len);
+ return TRUE;
+}
+
+static mrb_value
+str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ return mrb_str_beg_len(RSTRING_CHAR_LEN(str), &beg, &len) ?
+ str_subseq(mrb, str, beg, len) : mrb_nil_value();
}
MRB_API mrb_int
@@ -490,44 +664,28 @@ str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset)
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;
+ size_t len;
- check_frozen(mrb, s1);
+ mrb_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);
+ RSTR_COPY_ASCII_FLAG(s1, 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);
+ len = (size_t)RSTR_LEN(s2);
+ if (RSTR_EMBEDDABLE_P(len)) {
+ str_init_embed(s1, RSTR_PTR(s2), len);
}
else {
- str_make_shared(mrb, s2, s1);
+ str_share(mrb, s2, s1);
}
return mrb_obj_value(s1);
@@ -536,7 +694,7 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
static mrb_int
str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
{
- char *s, *sbeg, *t;
+ const char *s, *sbeg, *t;
struct RString *ps = mrb_str_ptr(str);
mrb_int len = RSTRING_LEN(sub);
@@ -549,11 +707,12 @@ str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
s = RSTR_PTR(ps) + pos;
t = RSTRING_PTR(sub);
if (len) {
+ s = char_adjust(sbeg, sbeg + RSTR_LEN(ps), s);
while (sbeg <= s) {
if (memcmp(s, t, len) == 0) {
return (mrb_int)(s - RSTR_PTR(ps));
}
- s--;
+ s = char_backtrack(sbeg, s);
}
return -1;
}
@@ -641,65 +800,17 @@ mrb_locale_from_utf8(const char *utf8, int len)
#endif
MRB_API void
-mrb_str_modify(mrb_state *mrb, struct RString *s)
+mrb_str_modify_keep_ascii(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;
+ mrb_check_frozen(mrb, s);
+ str_modify_keep_ascii(mrb, s);
+}
- 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 void
+mrb_str_modify(mrb_state *mrb, struct RString *s)
+{
+ mrb_str_modify_keep_ascii(mrb, s);
+ RSTR_UNSET_ASCII_FLAG(s);
}
MRB_API mrb_value
@@ -728,38 +839,18 @@ 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");
- }
-
+ check_null_byte(mrb, str0);
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 <i>str</i>.
- */
MRB_API void
mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
{
- if (!mrb_string_p(other)) {
- other = mrb_str_to_str(mrb, other);
- }
+ other = mrb_obj_as_string(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 <i>str</i>.
- */
MRB_API mrb_value
mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
{
@@ -777,10 +868,13 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
/* 15.2.10.5.2 */
/*
- * call-seq: (Caution! String("abcd") remain) for stack_argument
- * String("abcdefg") = String("abcd") + String("efg")
+ * call-seq:
+ * str + other_str -> new_str
*
- * Returns a new string object containing a copy of <i>str</i>.
+ * Concatenation---Returns a new <code>String</code> containing
+ * <i>other_str</i> concatenated to <i>str</i>.
+ *
+ * "Hello from " + self.to_s #=> "Hello from main"
*/
static mrb_value
mrb_str_plus_m(mrb_state *mrb, mrb_value self)
@@ -834,13 +928,12 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
if (times < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
}
- if (times && MRB_INT_MAX / times < RSTRING_LEN(self)) {
+ if (times && MRB_SSIZE_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);
@@ -852,6 +945,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
memcpy(p + n, p, len-n);
}
p[RSTR_LEN(str2)] = '\0';
+ RSTR_COPY_ASCII_FLAG(str2, mrb_str_ptr(self));
return mrb_obj_value(str2);
}
@@ -915,26 +1009,11 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2)
static mrb_value
mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
{
- mrb_value str2;
+ mrb_value str2 = mrb_get_arg1(mrb);
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);
- }
+ return mrb_nil_value();
}
else {
result = mrb_str_cmp(mrb, str1, str2);
@@ -956,15 +1035,7 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
MRB_API mrb_bool
mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
{
- if (mrb_immediate_p(str2)) return FALSE;
- if (!mrb_string_p(str2)) {
- if (mrb_nil_p(str2)) return FALSE;
- if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) {
- return FALSE;
- }
- str2 = mrb_funcall(mrb, str2, "to_str", 0);
- return mrb_equal(mrb, str2, str1);
- }
+ if (!mrb_string_p(str2)) return FALSE;
return str_eql(mrb, str1, str2);
}
@@ -982,54 +1053,26 @@ mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
static mrb_value
mrb_str_equal_m(mrb_state *mrb, mrb_value str1)
{
- mrb_value str2;
-
- mrb_get_args(mrb, "o", &str2);
+ mrb_value str2 = mrb_get_arg1(mrb);
return mrb_bool_value(mrb_str_equal(mrb, str1, str2));
}
/* ---------------------------------- */
-MRB_API mrb_value
-mrb_str_to_str(mrb_state *mrb, mrb_value str)
-{
- mrb_value s;
-
- if (!mrb_string_p(str)) {
- s = mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
- if (mrb_nil_p(s)) {
- s = mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s");
- }
- return s;
- }
- return str;
-}
+/* obslete: use RSTRING_PTR() */
MRB_API const char*
-mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
+mrb_string_value_ptr(mrb_state *mrb, mrb_value str)
{
- mrb_value str = mrb_str_to_str(mrb, ptr);
+ str = mrb_obj_as_string(mrb, str);
return RSTRING_PTR(str);
}
+/* obslete: use RSTRING_LEN() */
MRB_API mrb_int
mrb_string_value_len(mrb_state *mrb, mrb_value ptr)
{
- mrb_value str = mrb_str_to_str(mrb, ptr);
- return RSTRING_LEN(str);
-}
-
-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_to_str(mrb, ptr);
+ return RSTRING_LEN(ptr);
}
MRB_API mrb_value
@@ -1038,56 +1081,94 @@ 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;
+enum str_convert_range {
+ /* `beg` and `len` are byte unit in `0 ... str.bytesize` */
+ STR_BYTE_RANGE_CORRECTED = 1,
- mrb_regexp_check(mrb, indx);
- switch (mrb_type(indx)) {
- case MRB_TT_FIXNUM:
- idx = mrb_fixnum(indx);
+ /* `beg` and `len` are char unit in any range */
+ STR_CHAR_RANGE = 2,
-num_index:
- str = str_substr(mrb, str, idx, 1);
- if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
- return str;
+ /* `beg` and `len` are char unit in `0 ... str.size` */
+ STR_CHAR_RANGE_CORRECTED = 3,
- case MRB_TT_STRING:
- if (str_index_str(mrb, str, indx, 0) != -1)
- return mrb_str_dup(mrb, indx);
- return mrb_nil_value();
+ /* `beg` is out of range */
+ STR_OUT_OF_RANGE = -1
+};
+
+static enum str_convert_range
+str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len)
+{
+ if (!mrb_undef_p(alen)) {
+ *beg = mrb_as_int(mrb, indx);
+ *len = mrb_as_int(mrb, alen);
+ return STR_CHAR_RANGE;
+ }
+ else {
+ switch (mrb_type(indx)) {
+ case MRB_TT_INTEGER:
+ *beg = mrb_integer(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
- case MRB_TT_RANGE:
- goto range_arg;
+ case MRB_TT_STRING:
+ *beg = str_index_str(mrb, str, indx, 0);
+ if (*beg < 0) { break; }
+ *len = RSTRING_LEN(indx);
+ return STR_BYTE_RANGE_CORRECTED;
- 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();
+ case MRB_TT_RANGE:
+ goto range_arg;
+
+ default:
+ indx = mrb_to_int(mrb, indx);
+ if (mrb_integer_p(indx)) {
+ *beg = mrb_integer(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
+ }
+range_arg:
+ *len = RSTRING_CHAR_LEN(str);
+ switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) {
+ case MRB_RANGE_OK:
+ return STR_CHAR_RANGE_CORRECTED;
+ case MRB_RANGE_OUT:
+ return STR_OUT_OF_RANGE;
default:
break;
- }
}
- mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
+
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Integer");
+ }
+ }
+ return STR_OUT_OF_RANGE;
+}
+
+static mrb_value
+mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen)
+{
+ mrb_int beg, len;
+
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_CHAR_RANGE_CORRECTED:
+ return str_subseq(mrb, str, beg, len);
+ case STR_CHAR_RANGE:
+ str = str_substr(mrb, str, beg, len);
+ if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
+ return str;
+ case STR_BYTE_RANGE_CORRECTED:
+ if (mrb_string_p(indx)) {
+ return mrb_str_dup(mrb, indx);
}
- idx = mrb_fixnum(indx);
- goto num_index;
+ else {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
+ }
+ case STR_OUT_OF_RANGE:
+ default:
+ return mrb_nil_value();
}
- return mrb_nil_value(); /* not reached */
}
/* 15.2.10.5.6 */
@@ -1097,16 +1178,14 @@ num_index:
* 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 <code>Fixnum</code>, returns the code
- * of the character at that position. If passed two <code>Fixnum</code>
+ * Element Reference---If passed a single <code>Integer</code>, returns the code
+ * of the character at that position. If passed two <code>Integer</code>
* 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
@@ -1134,20 +1213,198 @@ 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;
+ if (mrb_get_args(mrb, "o|o", &a1, &a2) == 1) {
+ a2 = mrb_undef_value();
+ }
+
+ return mrb_str_aref(mrb, str, a1, a2);
+}
+
+static mrb_noreturn void
+str_out_of_index(mrb_state *mrb, mrb_value index)
+{
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %v out of string", index);
+}
- mrb_regexp_check(mrb, a1);
- mrb_get_args(mrb, "ii", &n1, &n2);
- return str_substr(mrb, str, n1, n2);
+static mrb_value
+str_replace_partial(mrb_state *mrb, mrb_value src, mrb_int pos, mrb_int end, mrb_value rep)
+{
+ const mrb_int shrink_threshold = 256;
+ struct RString *str = mrb_str_ptr(src);
+ mrb_int len = RSTR_LEN(str);
+ mrb_int replen, newlen;
+ char *strp;
+
+ if (end > len) { end = len; }
+
+ if (pos < 0 || pos > len) {
+ str_out_of_index(mrb, mrb_fixnum_value(pos));
+ }
+
+ replen = (mrb_nil_p(rep) ? 0 : RSTRING_LEN(rep));
+ newlen = replen + (len - (end - pos));
+
+ if (newlen >= MRB_SSIZE_MAX || newlen < replen /* overflowed */) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "string size too big");
+ }
+
+ mrb_str_modify(mrb, str);
+
+ if (len < newlen) {
+ resize_capa(mrb, str, newlen);
+ }
+
+ strp = RSTR_PTR(str);
+
+ memmove(strp + newlen - (len - end), strp + end, len - end);
+ if (!mrb_nil_p(rep)) {
+ memmove(strp + pos, RSTRING_PTR(rep), replen);
}
- if (argc != 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
+ RSTR_SET_LEN(str, newlen);
+ strp[newlen] = '\0';
+
+ if (len - newlen >= shrink_threshold) {
+ resize_capa(mrb, str, newlen);
}
- return mrb_str_aref(mrb, str, a1);
+
+ return src;
+}
+
+#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{'))
+
+static mrb_value
+str_escape(mrb_state *mrb, mrb_value str, mrb_bool inspect)
+{
+ const char *p, *pend;
+ char buf[4]; /* `\x??` or UTF-8 character */
+ mrb_value result = mrb_str_new_lit(mrb, "\"");
+#ifdef MRB_UTF8_STRING
+ uint32_t ascii_flag = MRB_STR_ASCII;
+#endif
+
+ p = RSTRING_PTR(str); pend = RSTRING_END(str);
+ for (;p < pend; p++) {
+ unsigned char c, cc;
+#ifdef MRB_UTF8_STRING
+ if (inspect) {
+ mrb_int clen = mrb_utf8len(p, pend);
+ if (clen > 1) {
+ mrb_int i;
+
+ for (i=0; i<clen; i++) {
+ buf[i] = p[i];
+ }
+ mrb_str_cat(mrb, result, buf, clen);
+ p += clen-1;
+ ascii_flag = 0;
+ continue;
+ }
+ }
+#endif
+ c = *p;
+ if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) {
+ buf[0] = '\\'; buf[1] = c;
+ mrb_str_cat(mrb, result, buf, 2);
+ continue;
+ }
+ if (ISPRINT(c)) {
+ buf[0] = c;
+ mrb_str_cat(mrb, result, buf, 1);
+ continue;
+ }
+ switch (c) {
+ case '\n': cc = 'n'; break;
+ case '\r': cc = 'r'; break;
+ case '\t': cc = 't'; break;
+ case '\f': cc = 'f'; break;
+ case '\013': cc = 'v'; break;
+ case '\010': cc = 'b'; break;
+ case '\007': cc = 'a'; break;
+ case 033: cc = 'e'; break;
+ default: cc = 0; break;
+ }
+ if (cc) {
+ buf[0] = '\\';
+ buf[1] = (char)cc;
+ mrb_str_cat(mrb, result, buf, 2);
+ continue;
+ }
+ else {
+ buf[0] = '\\';
+ buf[1] = 'x';
+ buf[3] = mrb_digitmap[c % 16]; c /= 16;
+ buf[2] = mrb_digitmap[c % 16];
+ mrb_str_cat(mrb, result, buf, 4);
+ continue;
+ }
+ }
+ mrb_str_cat_lit(mrb, result, "\"");
+#ifdef MRB_UTF8_STRING
+ if (inspect) {
+ mrb_str_ptr(str)->flags |= ascii_flag;
+ mrb_str_ptr(result)->flags |= ascii_flag;
+ }
+ else {
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(result));
+ }
+#endif
+
+ return result;
+}
+
+static void
+mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace)
+{
+ mrb_int beg, len, charlen;
+
+ mrb_to_str(mrb, replace);
+
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_OUT_OF_RANGE:
+ default:
+ mrb_raise(mrb, E_INDEX_ERROR, "string not matched");
+ case STR_CHAR_RANGE:
+ if (len < 0) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "negative length %v", alen);
+ }
+ charlen = RSTRING_CHAR_LEN(str);
+ if (beg < 0) { beg += charlen; }
+ if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); }
+ /* fall through */
+ case STR_CHAR_RANGE_CORRECTED:
+ str_range_to_bytes(str, &beg, &len);
+ /* fall through */
+ case STR_BYTE_RANGE_CORRECTED:
+ str_replace_partial(mrb, str, beg, beg + len, replace);
+ }
+}
+
+/*
+ * call-seq:
+ * str[fixnum] = replace
+ * str[fixnum, fixnum] = replace
+ * str[range] = replace
+ * str[other_str] = replace
+ *
+ * Modify +self+ by replacing the content of +self+.
+ * The portion of the string affected is determined using the same criteria as +String#[]+.
+ */
+static mrb_value
+mrb_str_aset_m(mrb_state *mrb, mrb_value str)
+{
+ mrb_value indx, alen, replace;
+
+ switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) {
+ case 2:
+ replace = alen;
+ alen = mrb_undef_value();
+ break;
+ case 3:
+ break;
+ }
+ mrb_str_aset(mrb, str, indx, alen, replace);
+ return str;
}
/* 15.2.10.5.8 */
@@ -1170,7 +1427,7 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(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)) {
@@ -1229,7 +1486,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
struct RString *s = mrb_str_ptr(str);
argc = mrb_get_args(mrb, "|S", &rs);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
len = RSTR_LEN(s);
if (argc == 0) {
if (len == 0) return mrb_nil_value();
@@ -1291,9 +1548,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
* str.chomp(separator="\n") => new_str
*
* Returns a new <code>String</code> with the given record separator removed
- * from the end of <i>str</i> (if present). If <code>$/</code> has not been
- * changed from the default Ruby record separator, then <code>chomp</code> also
- * removes carriage return characters (that is it will remove <code>\n</code>,
+ * from the end of <i>str</i> (if present). <code>chomp</code> also removes
+ * carriage return characters (that is it will remove <code>\n</code>,
* <code>\r</code>, and <code>\r\n</code>).
*
* "hello".chomp #=> "hello"
@@ -1328,14 +1584,14 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
{
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(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) {
- mrb_int clen = utf8len(p, e);
+ mrb_int clen = mrb_utf8len(p, e);
if (p + clen>=e) break;
p += clen;
}
@@ -1397,7 +1653,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTR_PTR(s);
pend = RSTR_PTR(s) + RSTR_LEN(s);
while (p < pend) {
@@ -1461,11 +1717,10 @@ mrb_str_empty_p(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_str_eql(mrb_state *mrb, mrb_value self)
{
- mrb_value str2;
+ mrb_value str2 = mrb_get_arg1(mrb);
mrb_bool eql_p;
- mrb_get_args(mrb, "o", &str2);
- eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2);
+ eql_p = (mrb_string_p(str2)) && str_eql(mrb, self, str2);
return mrb_bool_value(eql_p);
}
@@ -1483,13 +1738,17 @@ mrb_str_hash(mrb_state *mrb, mrb_value str)
struct RString *s = mrb_str_ptr(str);
mrb_int len = RSTR_LEN(s);
char *p = RSTR_PTR(s);
- uint64_t key = 0;
+ uint32_t hash = 0;
- while (len--) {
- key = key*65599 + *p;
- p++;
+ for(int i = 0; i < len; ++i) {
+ hash += p[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
}
- return (uint32_t)(key + (key>>5));
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
}
/* 15.2.10.5.20 */
@@ -1534,77 +1793,40 @@ mrb_str_include(mrb_state *mrb, mrb_value self)
/*
* 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
- * <i>substring</i>,
- * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>.
- * Returns
- * <code>nil</code> if not found.
+ * <i>substring</i>. Returns <code>nil</code> 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('l') #=> 2
* "hello".index('lo') #=> 3
* "hello".index('a') #=> nil
- * "hello".index(101) #=> 1(101=0x65='e')
- * "hello".index(/[aeiou]/, -3) #=> 4
+ * "hello".index('l', -2) #=> 3
*/
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_int pos;
- mrb_get_args(mrb, "*!", &argv, &argc);
- if (argc == 2) {
- mrb_get_args(mrb, "oi", &sub, &pos);
- }
- else {
+ if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) {
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) {
+ else if (pos < 0) {
+ mrb_int clen = RSTRING_CHAR_LEN(str);
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;
- }
+ pos = str_index_str_by_char(mrb, str, sub, pos);
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 */
/*
@@ -1651,7 +1873,7 @@ mrb_str_init(mrb_state *mrb, mrb_value self)
* str.to_sym => symbol
*
* Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the
- * symbol if it did not previously exist. See <code>Symbol#id2name</code>.
+ * symbol if it did not previously exist.
*
* "Koala".intern #=> :Koala
* s = 'cat'.to_sym #=> :cat
@@ -1673,15 +1895,20 @@ mrb_str_intern(mrb_state *mrb, mrb_value self)
MRB_API mrb_value
mrb_obj_as_string(mrb_state *mrb, mrb_value obj)
{
- mrb_value str;
-
- if (mrb_string_p(obj)) {
+ switch (mrb_type(obj)) {
+ case MRB_TT_STRING:
return obj;
+ case MRB_TT_SYMBOL:
+ return mrb_sym_str(mrb, mrb_symbol(obj));
+ case MRB_TT_INTEGER:
+ return mrb_integer_to_str(mrb, obj, 10);
+ case MRB_TT_SCLASS:
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ return mrb_mod_to_s(mrb, obj);
+ default:
+ return mrb_type_convert(mrb, obj, MRB_TT_STRING, MRB_SYM(to_s));
}
- 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
@@ -1714,16 +1941,16 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
return mrb_obj_value(p_str);
}
-MRB_API mrb_value
-mrb_string_type(mrb_state *mrb, mrb_value str)
+static inline void
+str_reverse(char *p, char *e)
{
- return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
-}
+ char c;
-MRB_API mrb_value
-mrb_check_string_type(mrb_state *mrb, mrb_value str)
-{
- return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
+ while (p < e) {
+ c = *p;
+ *p++ = *e;
+ *e-- = c;
+ }
}
/* 15.2.10.5.30 */
@@ -1736,53 +1963,38 @@ mrb_check_string_type(mrb_state *mrb, mrb_value str)
static mrb_value
mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
{
+ struct RString *s = mrb_str_ptr(str);
+ char *p, *e;
+
#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;
+ mrb_int len = RSTR_LEN(s);
+ if (utf8_len < 2) return str;
+ if (utf8_len < len) {
+ mrb_str_modify(mrb, s);
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s);
while (p<e) {
- mrb_int clen = utf8len(p, e);
- r -= clen;
- memcpy(r, p, clen);
+ mrb_int clen = mrb_utf8len(p, e);
+ str_reverse(p, p + clen - 1);
p += clen;
}
- mrb_free(mrb, buf);
+ goto bytes;
}
- return str;
-
- bytes:
#endif
- {
- struct RString *s = mrb_str_ptr(str);
- char *p, *e;
- char c;
+ if (RSTR_LEN(s) > 1) {
mrb_str_modify(mrb, s);
- if (RSTR_LEN(s) > 1) {
- p = RSTR_PTR(s);
- e = p + RSTR_LEN(s) - 1;
- while (p < e) {
- c = *p;
- *p++ = *e;
- *e-- = c;
- }
- }
- return str;
+ goto bytes;
}
+ return str;
+
+ bytes:
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s) - 1;
+ str_reverse(p, e);
+ return str;
}
/* ---------------------------------- */
@@ -1806,73 +2018,43 @@ mrb_str_reverse(mrb_state *mrb, mrb_value str)
/* 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
+ * str.rindex(substring [, offset]) => fixnum or nil
*
- * Returns the index of the last occurrence of the given <i>substring</i>,
- * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns
- * <code>nil</code> 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.
+ * Returns the index of the last occurrence of the given <i>substring</i>.
+ * Returns <code>nil</code> 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
+ * "hello".rindex('l', 2) #=> 2
*/
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 (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) {
+ pos = len;
+ }
+ else {
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)) */
+ pos = str_rindex(mrb, str, sub, pos);
+ if (pos >= 0) {
+ pos = bytes2chars(RSTRING_PTR(str), RSTRING_LEN(str), pos);
+ BYTES_ALIGN_CHECK(pos);
+ return mrb_fixnum_value(pos);
+ }
return mrb_nil_value();
}
@@ -1880,23 +2062,18 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
/*
* call-seq:
- * str.split(pattern="\n", [limit]) => anArray
+ * str.split(separator=nil, [limit]) => anArray
*
* Divides <i>str</i> into substrings based on a delimiter, returning an array
* of these substrings.
*
- * If <i>pattern</i> is a <code>String</code>, then its contents are used as
- * the delimiter when splitting <i>str</i>. If <i>pattern</i> is a single
+ * If <i>separator</i> is a <code>String</code>, then its contents are used as
+ * the delimiter when splitting <i>str</i>. If <i>separator</i> is a single
* space, <i>str</i> is split on whitespace, with leading whitespace and runs
* of contiguous whitespace characters ignored.
*
- * If <i>pattern</i> is a <code>Regexp</code>, <i>str</i> is divided where the
- * pattern matches. Whenever the pattern matches a zero-length string,
- * <i>str</i> is split into individual characters.
- *
- * If <i>pattern</i> is omitted, the value of <code>$;</code> is used. If
- * <code>$;</code> is <code>nil</code> (which is the default), <i>str</i> is
- * split on whitespace as if ' ' were specified.
+ * If <i>separator</i> is omitted or <code>nil</code> (which is the default),
+ * <i>str</i> is split on whitespace as if ' ' were specified.
*
* If the <i>limit</i> parameter is omitted, trailing null fields are
* suppressed. If <i>limit</i> is a positive number, at most that number of
@@ -1907,9 +2084,6 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
*
* " now's the time".split #=> ["now's", "the", "time"]
* " now's the time".split(' ') #=> ["now's", "the", "time"]
- * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"]
- * "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"]
@@ -1922,7 +2096,7 @@ 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;
+ enum {awk, string} split_type = string;
mrb_int i = 0;
mrb_int beg;
mrb_int end;
@@ -1944,16 +2118,11 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
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);
- }
+ else if (!mrb_string_p(spat)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "expected String");
+ }
+ else if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') {
+ split_type = awk;
}
result = mrb_ary_new(mrb);
@@ -1979,7 +2148,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
}
}
else if (ISSPACE(c)) {
- mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg));
+ mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, beg, end-beg));
mrb_gc_arena_restore(mrb, ai);
skip = TRUE;
beg = idx;
@@ -1990,7 +2159,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
}
}
}
- else if (split_type == string) {
+ else { /* split_type == string */
mrb_int str_len = RSTRING_LEN(str);
mrb_int pat_len = RSTRING_LEN(spat);
mrb_int idx = 0;
@@ -2004,22 +2173,19 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
else {
end = chars2bytes(str, idx, 1);
}
- mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end));
+ mrb_ary_push(mrb, result, mrb_str_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);
+ tmp = mrb_str_new(mrb, 0, 0);
}
else {
- tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
+ tmp = mrb_str_byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
}
mrb_ary_push(mrb, result, tmp);
}
@@ -2033,14 +2199,14 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
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)
+static mrb_value
+mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t 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 n = 0;
mrb_int val;
#define conv_digit(c) \
@@ -2117,7 +2283,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
break;
default:
if (base < 2 || 36 < base) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base);
}
break;
} /* end of switch (base) { */
@@ -2144,7 +2310,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
if (*(p - 1) == '0')
p--;
}
- if (p == pend) {
+ if (p == pend || *p == '_') {
if (badcheck) goto bad;
return mrb_fixnum_value(0);
}
@@ -2167,62 +2333,65 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
if (c < 0 || c >= 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));
+ if (mrb_int_mul_overflow(n, base, &n)) goto overflow;
+ if (MRB_INT_MAX - c < n) {
+ if (sign == 0 && MRB_INT_MAX - n == c - 1) {
+ n = MRB_INT_MIN;
+ sign = 1;
+ break;
}
+ overflow:
+ mrb_raisef(mrb, E_RANGE_ERROR, "string (%l) too big for integer", str, pend-str);
}
+ n += c;
}
val = (mrb_int)n;
if (badcheck) {
- if (p == str) goto bad; /* no number */
+ if (p == str) goto bad; /* no number */
+ if (*(p - 1) == '_') goto bad; /* trailing '_' */
while (p<pend && ISSPACE(*p)) p++;
- if (p<pend) goto bad; /* trailing garbage */
+ if (p<pend) goto bad; /* trailing garbage */
}
- return mrb_fixnum_value(sign ? val : -val);
+ return mrb_int_value(mrb, sign ? val : -val);
nullbyte:
mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
/* not reached */
bad:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)",
- mrb_inspect(mrb, mrb_str_new(mrb, str, pend-str)));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%!l)", str, pend-str);
/* not reached */
return mrb_fixnum_value(0);
}
-MRB_API mrb_value
-mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
-{
- return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck);
-}
-
+/* obslete: use RSTRING_CSTR() or mrb_string_cstr() */
MRB_API const char*
mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
{
- mrb_value str = mrb_str_to_str(mrb, *ptr);
- struct RString *ps = mrb_str_ptr(str);
- mrb_int len = mrb_str_strlen(mrb, ps);
- char *p = RSTR_PTR(ps);
+ struct RString *ps;
+ const char *p;
+ mrb_int len;
- if (!p || p[len] != '\0') {
- if (MRB_FROZEN_P(ps)) {
- *ptr = str = mrb_str_dup(mrb, str);
- ps = mrb_str_ptr(str);
- }
- mrb_str_modify(mrb, ps);
- return RSTR_PTR(ps);
+ check_null_byte(mrb, *ptr);
+ ps = mrb_str_ptr(*ptr);
+ p = RSTR_PTR(ps);
+ len = RSTR_LEN(ps);
+ if (p[len] == '\0') {
+ return p;
}
- return p;
+
+ /*
+ * Even after str_modify_keep_ascii(), NULL termination is not ensured if
+ * RSTR_SET_LEN() is used explicitly (e.g. String#delete_suffix!).
+ */
+ str_modify_keep_ascii(mrb, ps);
+ RSTR_PTR(ps)[len] = '\0';
+ return RSTR_PTR(ps);
+}
+
+MRB_API const char*
+mrb_string_cstr(mrb_state *mrb, mrb_value str)
+{
+ return mrb_string_value_cstr(mrb, &str);
}
MRB_API mrb_value
@@ -2231,7 +2400,8 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck)
const char *s;
mrb_int len;
- s = mrb_string_value_ptr(mrb, str);
+ mrb_to_str(mrb, str);
+ s = RSTRING_PTR(str);
len = RSTRING_LEN(str);
return mrb_str_len_to_inum(mrb, s, len, base, badcheck);
}
@@ -2263,72 +2433,98 @@ 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));
+ if (base < 0 || 36 < base) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", 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)
+#ifndef MRB_NO_FLOAT
+static double
+mrb_str_len_to_dbl(mrb_state *mrb, const char *s, size_t len, mrb_bool badcheck)
{
+ char buf[DBL_DIG * 4 + 20];
+ const char *p = s, *p2;
+ const char *pend = p + len;
char *end;
- char buf[DBL_DIG * 4 + 10];
+ char *n;
+ char prev = 0;
double d;
-
- enum {max_width = 20};
+ mrb_bool dot = FALSE;
if (!p) return 0.0;
- while (ISSPACE(*p)) p++;
-
- if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
- return 0.0;
+ while (p<pend && ISSPACE(*p)) p++;
+ p2 = p;
+
+ if (pend - p > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ mrb_value x;
+
+ if (!badcheck) return 0.0;
+ x = mrb_str_len_to_inum(mrb, p, pend-p, 0, badcheck);
+ if (mrb_integer_p(x))
+ d = (double)mrb_integer(x);
+ else /* if (mrb_float_p(x)) */
+ d = mrb_float(x);
+ return d;
+ }
+ while (p < pend) {
+ if (!*p) {
+ if (badcheck) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte");
+ /* not reached */
+ }
+ pend = p;
+ p = p2;
+ goto nocopy;
+ }
+ if (!badcheck && *p == ' ') {
+ pend = p;
+ p = p2;
+ goto nocopy;
+ }
+ if (*p == '_') break;
+ p++;
}
+ p = p2;
+ n = buf;
+ while (p < pend) {
+ char c = *p++;
+ if (c == '.') dot = TRUE;
+ if (c == '_') {
+ /* remove an underscore between digits */
+ if (n == buf || !ISDIGIT(prev) || p == pend) {
+ if (badcheck) goto bad;
+ break;
+ }
+ }
+ else if (badcheck && prev == '_' && !ISDIGIT(c)) goto bad;
+ else {
+ const char *bend = buf+sizeof(buf)-1;
+ if (n==bend) { /* buffer overflow */
+ if (dot) break; /* cut off remaining fractions */
+ return INFINITY;
+ }
+ *n++ = c;
+ }
+ prev = c;
+ }
+ *n = '\0';
+ p = buf;
+ pend = n;
+nocopy:
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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%!s)", s);
/* 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;
- }
+ if (badcheck) {
+ if (!end || p == end) goto bad;
+ while (end<pend && ISSPACE(*end)) end++;
+ if (end<pend) goto bad;
}
return d;
}
@@ -2336,22 +2532,7 @@ bad:
MRB_API double
mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
{
- char *s;
- mrb_int len;
-
- str = mrb_str_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);
+ return mrb_str_len_to_dbl(mrb, RSTRING_PTR(str), RSTRING_LEN(str), badcheck);
}
/* 15.2.10.5.39 */
@@ -2360,7 +2541,7 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
* str.to_f => float
*
* Returns the result of interpreting leading characters in <i>str</i> as a
- * floating point number. Extraneous characters past the end of a valid number
+ * 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 <i>str</i>,
* <code>0.0</code> is returned. This method never raises an exception.
*
@@ -2379,7 +2560,6 @@ mrb_str_to_f(mrb_state *mrb, mrb_value self)
/*
* call-seq:
* str.to_s => str
- * str.to_str => str
*
* Returns the receiver.
*/
@@ -2407,7 +2587,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
char *p, *pend;
mrb_bool modify = FALSE;
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTRING_PTR(str);
pend = RSTRING_END(str);
while (p < pend) {
@@ -2443,8 +2623,6 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self)
return str;
}
-#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{'))
-
/*
* call-seq:
* str.dump -> new_str
@@ -2455,113 +2633,7 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self)
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);
+ return str_escape(mrb, str, FALSE);
}
MRB_API mrb_value
@@ -2580,21 +2652,21 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
capa = RSTR_CAPA(s);
total = RSTR_LEN(s)+len;
- if (total >= MRB_INT_MAX) {
+ if (total >= MRB_SSIZE_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) {
+ if (capa <= MRB_SSIZE_MAX / 2) {
capa *= 2;
}
else {
capa = total+1;
}
}
- if (capa <= total || capa > MRB_INT_MAX) {
+ if (capa <= total || capa > MRB_SSIZE_MAX) {
goto size_error;
}
resize_capa(mrb, s, capa);
@@ -2603,7 +2675,7 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
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);
+ mrb_assert_int_fit(size_t, total, mrb_ssize, MRB_SSIZE_MAX);
RSTR_SET_LEN(s, total);
RSTR_PTR(s)[total] = '\0'; /* sentinel */
return str;
@@ -2612,7 +2684,7 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
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));
+ return mrb_str_cat(mrb, str, ptr, ptr ? strlen(ptr) : 0);
}
MRB_API mrb_value
@@ -2627,12 +2699,10 @@ mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2)
MRB_API mrb_value
mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2)
{
- str2 = mrb_str_to_str(mrb, 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
@@ -2647,68 +2717,7 @@ mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2)
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<clen; i++) {
- buf[i] = p[i];
- }
- mrb_str_cat(mrb, result, buf, clen);
- p += clen-1;
- continue;
- }
-#endif
- c = *p;
- if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) {
- buf[0] = '\\'; buf[1] = c;
- mrb_str_cat(mrb, result, buf, 2);
- continue;
- }
- if (ISPRINT(c)) {
- buf[0] = c;
- mrb_str_cat(mrb, result, buf, 1);
- continue;
- }
- switch (c) {
- case '\n': cc = 'n'; break;
- case '\r': cc = 'r'; break;
- case '\t': cc = 't'; break;
- case '\f': cc = 'f'; break;
- case '\013': cc = 'v'; break;
- case '\010': cc = 'b'; break;
- case '\007': cc = 'a'; break;
- case 033: cc = 'e'; break;
- default: cc = 0; break;
- }
- if (cc) {
- buf[0] = '\\';
- buf[1] = (char)cc;
- mrb_str_cat(mrb, result, buf, 2);
- continue;
- }
- else {
- buf[0] = '\\';
- buf[1] = 'x';
- buf[3] = mrb_digitmap[c % 16]; c /= 16;
- buf[2] = mrb_digitmap[c % 16];
- mrb_str_cat(mrb, result, buf, 4);
- continue;
- }
- }
- mrb_str_cat_lit(mrb, result, "\"");
-
- return result;
+ return str_escape(mrb, str, TRUE);
}
/*
@@ -2734,13 +2743,119 @@ mrb_str_bytes(mrb_state *mrb, mrb_value str)
return a;
}
+/*
+ * call-seq:
+ * str.getbyte(index) -> 0 .. 255
+ *
+ * returns the <i>index</i>th byte as an integer.
+ */
+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]);
+}
+
+/*
+ * call-seq:
+ * str.setbyte(index, integer) -> integer
+ *
+ * modifies the <i>index</i>th byte as <i>integer</i>.
+ */
+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 %i out of string", 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);
+}
+
+/*
+ * call-seq:
+ * str.byteslice(integer) -> new_str or nil
+ * str.byteslice(integer, integer) -> new_str or nil
+ * str.byteslice(range) -> new_str or nil
+ *
+ * Byte Reference---If passed a single Integer, returns a
+ * substring of one byte at that position. If passed two Integer
+ * 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
+ * bytes at offsets given by the range is returned. In all three cases, if
+ * an offset is negative, it is counted from the end of <i>str</i>. Returns
+ * <code>nil</code> if the initial offset falls outside the string, the length
+ * is negative, or the beginning of the range is greater than the end.
+ * The encoding of the resulted string keeps original encoding.
+ *
+ * "hello".byteslice(1) #=> "e"
+ * "hello".byteslice(-1) #=> "o"
+ * "hello".byteslice(1, 2) #=> "el"
+ * "\x80\u3042".byteslice(1, 3) #=> "\u3042"
+ * "\x03\u3042\xff".byteslice(1..3) #=> "\u3042"
+ */
+static mrb_value
+mrb_str_byteslice(mrb_state *mrb, mrb_value str)
+{
+ mrb_value a1;
+ mrb_int str_len = RSTRING_LEN(str), beg, len;
+ mrb_bool empty = TRUE;
+
+ len = mrb_get_argc(mrb);
+ switch (len) {
+ case 2:
+ mrb_get_args(mrb, "ii", &beg, &len);
+ break;
+ case 1:
+ a1 = mrb_get_arg1(mrb);
+ if (mrb_range_p(a1)) {
+ if (mrb_range_beg_len(mrb, a1, &beg, &len, str_len, TRUE) != MRB_RANGE_OK) {
+ return mrb_nil_value();
+ }
+ }
+ else {
+ beg = mrb_integer(mrb_to_int(mrb, a1));
+ len = 1;
+ empty = FALSE;
+ }
+ break;
+ default:
+ mrb_argnum_error(mrb, len, 1, 2);
+ break;
+ }
+ if (mrb_str_beg_len(str_len, &beg, &len) && (empty || len != 0)) {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
+ }
+ else {
+ return mrb_nil_value();
+ }
+}
+
/* ---------------------------*/
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_static_assert(RSTRING_EMBED_LEN_MAX < (1 << MRB_STR_EMBED_LEN_BIT),
+ "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);
@@ -2752,6 +2867,7 @@ mrb_init_string(mrb_state *mrb)
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, "[]=", mrb_str_aset_m, MRB_ARGS_ANY());
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 */
@@ -2765,7 +2881,7 @@ mrb_init_string(mrb_state *mrb)
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, "index", mrb_str_index_m, MRB_ARGS_ARG(1,1)); /* 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 */
@@ -2778,7 +2894,7 @@ mrb_init_string(mrb_state *mrb)
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
+#ifndef MRB_NO_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 */
@@ -2789,251 +2905,8 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */
mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE());
-}
-
-#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 <ctype.h>
-#include <errno.h>
-
-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;
+ 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_ARG(1,1));
}
-#endif
diff --git a/src/symbol.c b/src/symbol.c
index 6b4c7200c..007b8c885 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -11,65 +11,195 @@
#include <mruby/string.h>
#include <mruby/dump.h>
#include <mruby/class.h>
+#include <mruby/presym.h>
+
+#ifndef MRB_NO_PRESYM
+
+#ifndef MRB_PRESYM_SCANNING
+/* const uint16_t presym_length_table[] */
+/* const char * const presym_name_table[] */
+# include <mruby/presym/table.h>
+#endif
+
+static mrb_sym
+presym_find(const char *name, size_t len)
+{
+ if (presym_length_table[MRB_PRESYM_MAX-1] < len) return 0;
+
+ mrb_sym start, idx, presym_size = MRB_PRESYM_MAX;
+ int cmp;
+ for (start = 0; presym_size != 0; presym_size/=2) {
+ idx = start+presym_size/2;
+ cmp = (int)len-(int)presym_length_table[idx];
+ if (cmp == 0) {
+ cmp = memcmp(name, presym_name_table[idx], len);
+ if (cmp == 0) return idx+1;
+ }
+ if (0 < cmp) {
+ start = ++idx;
+ --presym_size;
+ }
+ }
+ return 0;
+}
+
+static const char*
+presym_sym2name(mrb_sym sym, mrb_int *lenp)
+{
+ if (sym > MRB_PRESYM_MAX) return NULL;
+ if (lenp) *lenp = presym_length_table[sym-1];
+ return presym_name_table[sym-1];
+}
+
+#endif /* MRB_NO_PRESYM */
/* ------------------------------------------------------ */
typedef struct symbol_name {
mrb_bool lit : 1;
+ uint8_t prev;
uint16_t len;
const char *name;
} symbol_name;
-static inline khint_t
-sym_hash_func(mrb_state *mrb, mrb_sym s)
+static void
+sym_validate_len(mrb_state *mrb, size_t len)
+{
+ if (len >= UINT16_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ }
+}
+
+#ifdef MRB_USE_ALL_SYMBOLS
+# define SYMBOL_INLINE_P(sym) FALSE
+# define sym_inline_pack(name, len) 0
+# define sym_inline_unpack(sym, buf, lenp) NULL
+#else
+# define SYMBOL_INLINE_P(sym) ((sym) >= (1<<24))
+
+static const char pack_table[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+static mrb_sym
+sym_inline_pack(const char *name, size_t len)
{
- khint_t h = 0;
- size_t i, len = mrb->symtbl[s].len;
- const char *p = mrb->symtbl[s].name;
+ const size_t pack_length_max = 5;
+
+ char c;
+ const char *p;
+ size_t i;
+ mrb_sym sym = 0;
+ if (len > pack_length_max) return 0; /* too long */
+ if (len == 0) return 0; /* empty string */
for (i=0; i<len; i++) {
- h = (h << 5) - h + *p++;
+ uint32_t bits;
+
+ c = name[i];
+ if (c == 0) return 0; /* NUL in name */
+ p = strchr(pack_table, (int)c);
+ if (p == 0) return 0; /* non alnum char */
+ bits = (uint32_t)(p - pack_table)+1;
+ sym |= bits<<(24-i*6);
}
- return h;
+ mrb_assert(SYMBOL_INLINE_P(sym));
+ return sym;
}
-#define sym_hash_equal(mrb,a, b) (mrb->symtbl[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)
+static const char*
+sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp)
{
- if (len >= RITE_LV_NULL_MARK) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ int i;
+
+ mrb_assert(SYMBOL_INLINE_P(sym));
+
+ for (i=0; i<5; i++) {
+ uint32_t bits = sym>>(24-i*6) & 0x3f;
+ if (bits == 0) break;
+ buf[i] = pack_table[bits-1];;
}
+ buf[i] = '\0';
+ if (lenp) *lenp = i;
+ return buf;
+}
+#endif
+
+static uint8_t
+symhash(const char *key, size_t len)
+{
+ uint32_t hash, i;
+
+ for(hash = i = 0; i < len; ++i) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash & 0xff;
+}
+
+static mrb_sym
+find_symbol(mrb_state *mrb, const char *name, size_t len, uint8_t *hashp)
+{
+ mrb_sym i;
+ symbol_name *sname;
+ uint8_t hash;
+
+#ifndef MRB_NO_PRESYM
+ /* presym */
+ i = presym_find(name, len);
+ if (i > 0) return i;
+#endif
+
+ /* inline symbol */
+ i = sym_inline_pack(name, len);
+ if (i > 0) return i;
+
+ hash = symhash(name, len);
+ if (hashp) *hashp = hash;
+
+ i = mrb->symhash[hash];
+ if (i == 0) return 0;
+ do {
+ sname = &mrb->symtbl[i];
+ if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+ return (i+MRB_PRESYM_MAX);
+ }
+ if (sname->prev == 0xff) {
+ i -= 0xff;
+ sname = &mrb->symtbl[i];
+ while (mrb->symtbl < sname) {
+ if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+ return (mrb_sym)((sname - mrb->symtbl)+MRB_PRESYM_MAX);
+ }
+ sname--;
+ }
+ return 0;
+ }
+ i -= sname->prev;
+ } while (sname->prev > 0);
+ return 0;
}
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;
+ symbol_name *sname;
+ uint8_t hash;
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);
- }
+ sym = find_symbol(mrb, name, len, &hash);
+ if (sym > 0) return sym;
/* registering a new symbol */
- sym = ++mrb->symidx;
+ sym = mrb->symidx + 1;
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));
+ size_t symcapa = mrb->symcapa;
+ if (symcapa == 0) symcapa = 100;
+ else symcapa = (size_t)(symcapa * 6 / 5);
+ mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(symcapa+1));
+ mrb->symcapa = symcapa;
}
sname = &mrb->symtbl[sym];
sname->len = (uint16_t)len;
@@ -78,15 +208,25 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
sname->lit = TRUE;
}
else {
- p = (char *)mrb_malloc(mrb, len+1);
+ char *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);
+ if (mrb->symhash[hash]) {
+ mrb_sym i = sym - mrb->symhash[hash];
+ if (i > 0xff)
+ sname->prev = 0xff;
+ else
+ sname->prev = i;
+ }
+ else {
+ sname->prev = 0;
+ }
+ mrb->symhash[hash] = mrb->symidx = sym;
- return sym;
+ return (sym+MRB_PRESYM_MAX);
}
MRB_API mrb_sym
@@ -113,41 +253,69 @@ 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)
+MRB_API mrb_sym
+mrb_intern_check(mrb_state *mrb, const char *name, size_t len)
{
- khash_t(n2s) *h = mrb->name2sym;
- symbol_name *sname = mrb->symtbl;
- khiter_t k;
+ mrb_sym sym;
sym_validate_len(mrb, len);
- sname->len = (uint16_t)len;
- sname->name = name;
+ sym = find_symbol(mrb, name, len, NULL);
+ if (sym > 0) return sym;
+ return 0;
+}
- 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(mrb_state *mrb, const char *name, size_t len)
+{
+ mrb_sym sym = mrb_intern_check(mrb, name, len);
+ if (sym == 0) return mrb_nil_value();
+ return mrb_symbol_value(sym);
+}
+
+MRB_API mrb_sym
+mrb_intern_check_cstr(mrb_state *mrb, const char *name)
+{
+ return mrb_intern_check(mrb, name, strlen(name));
}
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_sym sym = mrb_intern_check_cstr(mrb, name);
+ if (sym == 0) return mrb_nil_value();
+ return mrb_symbol_value(sym);
+}
+
+MRB_API mrb_sym
+mrb_intern_check_str(mrb_state *mrb, mrb_value str)
+{
+ return mrb_intern_check(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
}
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));
+ mrb_sym sym = mrb_intern_check_str(mrb, str);
+ if (sym == 0) return mrb_nil_value();
+ return mrb_symbol_value(sym);
}
-/* 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)
+static const char*
+sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp)
{
- if (sym == 0 || mrb->symidx < sym) {
+ if (sym == 0) goto outofsym;
+ if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp);
+
+#ifndef MRB_NO_PRESYM
+ {
+ const char *name = presym_sym2name(sym, lenp);
+ if (name) return name;
+ }
+#endif
+ sym -= MRB_PRESYM_MAX;
+
+ if (mrb->symidx < sym) {
+ outofsym:
if (lenp) *lenp = 0;
return NULL;
}
@@ -156,6 +324,16 @@ mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
return mrb->symtbl[sym].name;
}
+MRB_API const char*
+mrb_sym_name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
+{
+#ifdef MRB_USE_ALL_SYMBOLS
+ return sym2name_len(mrb, sym, NULL, lenp);
+#else
+ return sym2name_len(mrb, sym, mrb->symbuf, lenp);
+#endif
+}
+
void
mrb_free_symtbl(mrb_state *mrb)
{
@@ -167,13 +345,11 @@ mrb_free_symtbl(mrb_state *mrb)
}
}
mrb_free(mrb, mrb->symtbl);
- kh_destroy(n2s, mrb, mrb->name2sym);
}
void
mrb_init_symtbl(mrb_state *mrb)
{
- mrb->name2sym = kh_init(n2s, mrb);
}
/**********************************************************************
@@ -209,46 +385,44 @@ mrb_init_symtbl(mrb_state *mrb)
*
*/
-
-/* 15.2.11.3.1 */
+/* 15.2.11.3.2 */
+/* 15.2.11.3.3 */
/*
* call-seq:
- * sym == obj -> true or false
+ * sym.to_s -> string
*
- * Equality---If <i>sym</i> and <i>obj</i> are exactly the same
- * symbol, returns <code>true</code>.
+ * Returns the name or string corresponding to <i>sym</i>.
+ *
+ * :fred.to_s #=> "fred"
*/
-
static mrb_value
-sym_equal(mrb_state *mrb, mrb_value sym1)
+sym_to_s(mrb_state *mrb, mrb_value sym)
{
- mrb_value sym2;
-
- mrb_get_args(mrb, "o", &sym2);
-
- return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2));
+ return mrb_sym_str(mrb, mrb_symbol(sym));
}
-/* 15.2.11.3.2 */
-/* 15.2.11.3.3 */
/*
* call-seq:
- * sym.id2name -> string
- * sym.to_s -> string
+ * sym.name -> string
*
- * Returns the name or string corresponding to <i>sym</i>.
+ * Returns the name or string corresponding to <i>sym</i>. Unlike #to_s, the
+ * returned string is frozen.
*
- * :fred.id2name #=> "fred"
+ * :fred.name #=> "fred"
+ * :fred.name.frozen? #=> true
*/
static mrb_value
-mrb_sym_to_s(mrb_state *mrb, mrb_value sym)
+sym_name(mrb_state *mrb, mrb_value vsym)
{
- mrb_sym id = mrb_symbol(sym);
- const char *p;
+ mrb_sym sym = mrb_symbol(vsym);
mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, sym, &len);
- p = mrb_sym2name_len(mrb, id, &len);
- return mrb_str_new_static(mrb, p, len);
+ mrb_assert(name != NULL);
+ if (SYMBOL_INLINE_P(sym)) {
+ return mrb_str_new_frozen(mrb, name, len);
+ }
+ return mrb_str_new_static_frozen(mrb, name, len);
}
/* 15.2.11.3.4 */
@@ -403,57 +577,77 @@ sym_inspect(mrb_state *mrb, mrb_value sym)
mrb_sym id = mrb_symbol(sym);
char *sp;
- name = mrb_sym2name_len(mrb, id, &len);
- str = mrb_str_new(mrb, 0, len+1);
+ name = mrb_sym_name_len(mrb, id, &len);
+ str = mrb_str_new(mrb, NULL, len+1);
sp = RSTRING_PTR(str);
- RSTRING_PTR(str)[0] = ':';
+ sp[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);
+ str = mrb_str_inspect(mrb, str);
sp = RSTRING_PTR(str);
sp[0] = ':';
sp[1] = '"';
}
+#ifdef MRB_UTF8_STRING
+ if (SYMBOL_INLINE_P(id)) RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+#endif
return str;
}
MRB_API mrb_value
-mrb_sym2str(mrb_state *mrb, mrb_sym sym)
+mrb_sym_str(mrb_state *mrb, mrb_sym sym)
{
mrb_int len;
- const char *name = mrb_sym2name_len(mrb, sym, &len);
+ const char *name = mrb_sym_name_len(mrb, sym, &len);
if (!name) return mrb_undef_value(); /* can't happen */
+ if (SYMBOL_INLINE_P(sym)) {
+ mrb_value str = mrb_str_new(mrb, name, len);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
+ }
return mrb_str_new_static(mrb, name, len);
}
-MRB_API const char*
-mrb_sym2name(mrb_state *mrb, mrb_sym sym)
+static const char*
+sym_cstr(mrb_state *mrb, mrb_sym sym, mrb_bool dump)
{
mrb_int len;
- const char *name = mrb_sym2name_len(mrb, sym, &len);
+ const char *name = mrb_sym_name_len(mrb, sym, &len);
if (!name) return NULL;
- if (symname_p(name) && strlen(name) == (size_t)len) {
+ if (strlen(name) == (size_t)len && (!dump || symname_p(name))) {
return name;
}
else {
- mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len));
+ mrb_value str = mrb_str_new_static(mrb, name, len);
+ str = mrb_str_dump(mrb, str);
return RSTRING_PTR(str);
}
}
+MRB_API const char*
+mrb_sym_name(mrb_state *mrb, mrb_sym sym)
+{
+ return sym_cstr(mrb, sym, FALSE);
+}
+
+MRB_API const char*
+mrb_sym_dump(mrb_state *mrb, mrb_sym sym)
+{
+ return sym_cstr(mrb, sym, TRUE);
+}
+
#define lesser(a,b) (((a)>(b))?(b):(a))
static mrb_value
sym_cmp(mrb_state *mrb, mrb_value s1)
{
- mrb_value s2;
+ mrb_value s2 = mrb_get_arg1(mrb);
mrb_sym sym1, sym2;
- mrb_get_args(mrb, "o", &s2);
- if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value();
+ if (!mrb_symbol_p(s2)) return mrb_nil_value();
sym1 = mrb_symbol(s1);
sym2 = mrb_symbol(s2);
if (sym1 == sym2) return mrb_fixnum_value(0);
@@ -461,9 +655,10 @@ sym_cmp(mrb_state *mrb, mrb_value s1)
const char *p1, *p2;
int retval;
mrb_int len, len1, len2;
+ char buf1[8], buf2[8];
- p1 = mrb_sym2name_len(mrb, sym1, &len1);
- p2 = mrb_sym2name_len(mrb, sym2, &len2);
+ p1 = sym2name_len(mrb, sym1, buf1, &len1);
+ p2 = sym2name_len(mrb, sym2, buf2, &len2);
len = lesser(len1, len2);
retval = memcmp(p1, p2, len);
if (retval == 0) {
@@ -481,14 +676,13 @@ 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->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));
+ mrb_define_method(mrb, sym, "to_s", sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */
+ mrb_define_method(mrb, sym, "name", sym_name, MRB_ARGS_NONE());
+ 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));
}
diff --git a/src/value_array.h b/src/value_array.h
index bc5f28b06..6089b8aa0 100644
--- a/src/value_array.h
+++ b/src/value_array.h
@@ -6,6 +6,7 @@
static inline void
value_move(mrb_value *s1, const mrb_value *s2, size_t n)
{
+ if (n == 0) return;
if (s1 > s2 && s1 < s2 + n)
{
s1 += n;
diff --git a/src/variable.c b/src/variable.c
index 72c13aa1f..d89295229 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -9,24 +9,19 @@
#include <mruby/class.h>
#include <mruby/proc.h>
#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/presym.h>
-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;
+struct iv_elem {
+ mrb_sym key;
+ mrb_value val;
+};
/* Instance variable table structure */
typedef struct iv_tbl {
- segment *rootseg;
size_t size;
- size_t last_len;
+ size_t alloc;
+ struct iv_elem *table;
} iv_tbl;
/* Creates the instance variable table. */
@@ -37,67 +32,82 @@ iv_new(mrb_state *mrb)
t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
t->size = 0;
- t->rootseg = NULL;
- t->last_len = 0;
+ t->alloc = 0;
+ t->table = NULL;
return t;
}
+static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val);
+
+static void
+iv_rehash(mrb_state *mrb, iv_tbl *t)
+{
+ size_t old_alloc = t->alloc;
+ size_t new_alloc = old_alloc+1;
+ struct iv_elem *old_table = t->table;
+
+ khash_power2(new_alloc);
+ if (old_alloc == new_alloc) return;
+
+ t->alloc = new_alloc;
+ t->size = 0;
+ t->table = (struct iv_elem*)mrb_calloc(mrb, sizeof(struct iv_elem), new_alloc);
+
+ for (size_t i = 0; i < old_alloc; i++) {
+ struct iv_elem *slot = &old_table[i];
+
+ /* key = 0 means empty; val = undef means deleted */
+ if (slot->key != 0 && !mrb_undef_p(slot->val)) {
+ iv_put(mrb, t, slot->key, slot->val);
+ }
+ }
+ mrb_free(mrb, old_table);
+}
+
+#define slot_empty_p(slot) ((slot)->key == 0 && !mrb_undef_p((slot)->val))
+
/* 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;
+ size_t hash, pos, start;
+ struct iv_elem *dslot = NULL;
if (t == NULL) return;
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[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;
+ if (t->alloc == 0) {
+ iv_rehash(mrb, t);
+ }
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct iv_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ slot->val = val;
+ return;
+ }
+ else if (slot_empty_p(slot)) {
+ t->size++;
+ slot->key = sym;
+ slot->val = val;
+ return;
+ }
+ else if (!dslot && mrb_undef_p(slot->val)) { /* deleted */
+ dslot = slot;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ if (dslot) {
t->size++;
+ dslot->key = sym;
+ dslot->val = val;
return;
}
- if (!matched_seg && key == 0) {
- matched_seg = seg;
- matched_idx = i;
- }
- else if (key == sym) {
- seg->val[i] = val;
- return;
- }
+ /* no room */
+ iv_rehash(mrb, t);
+ start = pos = hash & (t->alloc-1);
}
- 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;
}
}
@@ -105,133 +115,111 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
static mrb_bool
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
- segment *seg;
- size_t i;
+ size_t hash, pos, start;
if (t == NULL) return FALSE;
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
+ if (t->alloc == 0) return FALSE;
+ if (t->size == 0) return FALSE;
- if (!seg->next && i >= t->last_len) {
- return FALSE;
- }
- if (key == sym) {
- if (vp) *vp = seg->val[i];
- return TRUE;
- }
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct iv_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ if (vp) *vp = slot->val;
+ return TRUE;
+ }
+ else if (slot_empty_p(slot)) {
+ return FALSE;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return FALSE;
}
- 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;
+ size_t hash, pos, start;
if (t == NULL) return FALSE;
- seg = t->rootseg;
- while (seg) {
- for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
+ if (t->alloc == 0) return FALSE;
+ if (t->size == 0) return FALSE;
- 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;
- }
+ hash = kh_int_hash_func(mrb, sym);
+ start = pos = hash & (t->alloc-1);
+ for (;;) {
+ struct iv_elem *slot = &t->table[pos];
+
+ if (slot->key == sym) {
+ if (vp) *vp = slot->val;
+ t->size--;
+ slot->key = 0;
+ slot->val = mrb_undef_value();
+ return TRUE;
+ }
+ else if (slot_empty_p(slot)) {
+ return FALSE;
+ }
+ pos = (pos+1) & (t->alloc-1);
+ if (pos == start) { /* not found */
+ return FALSE;
}
- 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)
+static void
+iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_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; i<MRB_IV_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
+ if (t == NULL) return;
+ if (t->alloc == 0) return;
+ if (t->size == 0) return;
- /* 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;
- }
+ for (i=0; i<t->alloc; i++) {
+ struct iv_elem *slot = &t->table[i];
+
+ if (slot->key && !mrb_undef_p(slot->val)) {
+ if ((*func)(mrb, slot->key, slot->val, p) != 0) {
+ return;
}
}
- seg = seg->next;
}
- return TRUE;
+ return;
}
/* 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;
+ return t->size;
}
/* 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);
+ if (t == NULL) return NULL;
+ if (t->alloc == 0) return NULL;
+ if (t->size == 0) return NULL;
- while (seg != NULL) {
- for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
- mrb_sym key = seg->key[i];
- mrb_value val = seg->val[i];
+ t2 = iv_new(mrb);
+ for (i=0; i<t->alloc; i++) {
+ struct iv_elem *slot = &t->table[i];
- if ((seg->next == NULL) && (i >= t->last_len)) {
- return t2;
- }
- iv_put(mrb, t2, key, val);
+ if (slot->key && !mrb_undef_p(slot->val)) {
+ iv_put(mrb, t2, slot->key, slot->val);
}
- seg = seg->next;
}
return t2;
}
@@ -240,14 +228,7 @@ iv_copy(mrb_state *mrb, iv_tbl *t)
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->table);
mrb_free(mrb, t);
}
@@ -346,21 +327,30 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
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)
+void
+mrb_obj_iv_set_force(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);
+ iv_put(mrb, obj->iv, sym, v);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)obj, v);
+}
+
+MRB_API void
+mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+{
+ mrb_check_frozen(mrb, obj);
+ mrb_obj_iv_set_force(mrb, obj, sym, v);
+}
+
+/* Iterates over the instance variable table. */
+MRB_API void
+mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p)
+{
+ if (!obj_iv_p(obj)) return;
+ iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p);
}
static inline mrb_bool
@@ -374,20 +364,20 @@ 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__");
+ if (obj != c && ISUPPER(mrb_sym_name_len(mrb, sym, NULL)[0])) {
+ mrb_sym id_classname = MRB_SYM(__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__");
+ mrb_sym id_outer = MRB_SYM(__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));
+ mrb_obj_iv_set_force(mrb, c, id_classname, mrb_symbol_value(sym));
}
else {
- mrb_obj_iv_set(mrb, c, id_outer, mrb_obj_value(obj));
+ mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj));
}
}
}
@@ -425,29 +415,24 @@ mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
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;
+ mrb_int len;
- s = mrb_sym2name_len(mrb, iv_name, &len);
+ s = mrb_sym_name_len(mrb, iv_name, &len);
if (len < 2) return FALSE;
if (s[0] != '@') return FALSE;
- if (s[1] == '@') return FALSE;
- for (i=1; i<len; i++) {
- if (!identchar(s[i])) return FALSE;
- }
- return TRUE;
+ if (ISDIGIT(s[1])) return FALSE;
+ return mrb_ident_p(s+1, len-1);
}
MRB_API void
mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
{
if (!mrb_iv_name_sym_p(mrb, iv_name)) {
- mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name));
+ mrb_name_error(mrb, iv_name, "'%n' is not allowed as an instance variable name", iv_name);
}
}
@@ -484,10 +469,10 @@ inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
else {
mrb_str_cat_lit(mrb, str, ", ");
}
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
mrb_str_cat(mrb, str, s, len);
mrb_str_cat_lit(mrb, str, "=");
- if (mrb_type(v) == MRB_TT_OBJECT) {
+ if (mrb_object_p(v)) {
ins = mrb_any_to_s(mrb, v);
}
else {
@@ -510,7 +495,7 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj)
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));
+ mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, obj));
iv_foreach(mrb, t, inspect_i, &str);
mrb_str_cat_lit(mrb, str, ">");
@@ -526,6 +511,7 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
iv_tbl *t = mrb_obj_ptr(obj)->iv;
mrb_value val;
+ mrb_check_frozen(mrb, mrb_obj_ptr(obj));
if (iv_del(mrb, t, sym, &val)) {
return val;
}
@@ -541,7 +527,7 @@ iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_int len;
ary = *(mrb_value*)p;
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
if (len > 1 && s[0] == '@' && s[1] != '@') {
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
@@ -585,7 +571,7 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_int len;
ary = *(mrb_value*)p;
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
if (len > 2 && s[0] == '@' && s[1] == '@') {
mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
@@ -595,7 +581,7 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
/* 15.2.2.4.19 */
/*
* call-seq:
- * mod.class_variables -> array
+ * mod.class_variables(inherit=true) -> array
*
* Returns an array of the names of class variables in <i>mod</i>.
*
@@ -613,17 +599,20 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
{
mrb_value ary;
struct RClass *c;
+ mrb_bool inherit = TRUE;
+ mrb_get_args(mrb, "|b", &inherit);
ary = mrb_ary_new(mrb);
c = mrb_class_ptr(mod);
while (c) {
iv_foreach(mrb, c->iv, cv_i, &ary);
+ if (!inherit) break;
c = c->super;
}
return ary;
}
-MRB_API mrb_value
+mrb_value
mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
{
struct RClass * cls = c;
@@ -640,8 +629,7 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
if (cls && cls->tt == MRB_TT_SCLASS) {
mrb_value klass;
- klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
- mrb_intern_lit(mrb, "__attached__"));
+ klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, MRB_SYM(__attached__));
c = mrb_class_ptr(klass);
if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
given = FALSE;
@@ -654,8 +642,7 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
if (given) return v;
}
}
- mrb_name_error(mrb, sym, "uninitialized class variable %S in %S",
- mrb_sym2str(mrb, sym), mrb_obj_value(cls));
+ mrb_name_error(mrb, sym, "uninitialized class variable %n in %C", sym, cls);
/* not reached */
return mrb_nil_value();
}
@@ -675,8 +662,9 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
iv_tbl *t = c->iv;
if (iv_get(mrb, t, sym, NULL)) {
+ mrb_check_frozen(mrb, c);
iv_put(mrb, t, sym, v);
- mrb_write_barrier(mrb, (struct RBasic*)c);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v);
return;
}
c = c->super;
@@ -685,8 +673,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value 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__"));
+ klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
switch (mrb_type(klass)) {
case MRB_TT_CLASS:
case MRB_TT_MODULE:
@@ -702,12 +689,13 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
c = cls;
}
+ mrb_check_frozen(mrb, c);
if (!c->iv) {
c->iv = iv_new(mrb);
}
iv_put(mrb, c->iv, sym, v);
- mrb_write_barrier(mrb, (struct RBasic*)c);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v);
}
MRB_API void
@@ -716,7 +704,7 @@ 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_bool
mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
{
while (c) {
@@ -739,7 +727,13 @@ mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym)
{
struct RClass *c;
- c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ const struct RProc *p = mrb->c->ci->proc;
+
+ for (;;) {
+ c = MRB_PROC_TARGET_CLASS(p);
+ if (c && c->tt != MRB_TT_SCLASS) break;
+ p = p->upper;
+ }
return mrb_mod_cv_get(mrb, c, sym);
}
@@ -747,8 +741,13 @@ void
mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
struct RClass *c;
+ const struct RProc *p = mrb->c->ci->proc;
- c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ for (;;) {
+ c = MRB_PROC_TARGET_CLASS(p);
+ if (c && c->tt != MRB_TT_SCLASS) break;
+ p = p->upper;
+ }
mrb_mod_cv_set(mrb, c, sym, v);
}
@@ -767,16 +766,17 @@ mod_const_check(mrb_state *mrb, mrb_value mod)
}
static mrb_value
-const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
+const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool skip)
{
struct RClass *c = base;
mrb_value v;
mrb_bool retry = FALSE;
mrb_value name;
+ if (skip) c = c->super;
L_RETRY:
while (c) {
- if (c->iv) {
+ if (!MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_PREPENDED) && c->iv) {
if (iv_get(mrb, c->iv, sym, &v))
return v;
}
@@ -788,14 +788,14 @@ L_RETRY:
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);
+ return mrb_funcall_argv(mrb, mrb_obj_value(base), MRB_SYM(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);
+ return const_get(mrb, mrb_class_ptr(mod), sym, FALSE);
}
mrb_value
@@ -804,9 +804,10 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
struct RClass *c;
struct RClass *c2;
mrb_value v;
- struct RProc *proc;
+ const struct RProc *proc = mrb->c->ci->proc;
- c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ c = MRB_PROC_TARGET_CLASS(proc);
+ if (!c) c = mrb->object_class;
if (iv_get(mrb, c->iv, sym, &v)) {
return v;
}
@@ -814,15 +815,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
while (c2 && c2->tt == MRB_TT_SCLASS) {
mrb_value klass;
- if (!iv_get(mrb, c2->iv, mrb_intern_lit(mrb, "__attached__"), &klass)) {
+ if (!iv_get(mrb, c2->iv, MRB_SYM(__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;
+ proc = proc->upper;
while (proc) {
c2 = MRB_PROC_TARGET_CLASS(proc);
if (c2 && iv_get(mrb, c2->iv, sym, &v)) {
@@ -830,7 +830,7 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
}
proc = proc->upper;
}
- return const_get(mrb, c, sym);
+ return const_get(mrb, c, sym, TRUE);
}
MRB_API void
@@ -849,6 +849,7 @@ 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);
+ if (!c) c = mrb->object_class;
mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v);
}
@@ -860,6 +861,12 @@ mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym)
}
MRB_API void
+mrb_define_const_id(mrb_state *mrb, struct RClass *mod, mrb_sym name, mrb_value v)
+{
+ mrb_obj_iv_set(mrb, (struct RObject*)mod, name, v);
+}
+
+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);
@@ -879,9 +886,17 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
mrb_int len;
ary = *(mrb_value*)p;
- s = mrb_sym2name_len(mrb, sym, &len);
+ s = mrb_sym_name_len(mrb, sym, &len);
if (len >= 1 && ISUPPER(s[0])) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
+ mrb_int i, alen = RARRAY_LEN(ary);
+
+ for (i=0; i<alen; i++) {
+ if (mrb_symbol(RARRAY_PTR(ary)[i]) == sym)
+ break;
+ }
+ if (i==alen) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
+ }
}
return 0;
}
@@ -891,7 +906,7 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
* call-seq:
* mod.constants -> array
*
- * Returns an array of all names of contants defined in the receiver.
+ * Returns an array of all names of constants defined in the receiver.
*/
mrb_value
mrb_mod_constants(mrb_state *mrb, mrb_value mod)
@@ -964,16 +979,8 @@ 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;
}
@@ -1055,7 +1062,7 @@ 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__"));
+ ov = mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__outer__));
if (mrb_nil_p(ov)) return NULL;
switch (mrb_type(ov)) {
case MRB_TT_CLASS:
@@ -1102,12 +1109,34 @@ mrb_class_find_path(mrb_state *mrb, struct RClass *c)
mrb_str_cat_cstr(mrb, path, str);
mrb_str_cat_cstr(mrb, path, "::");
- str = mrb_sym2name_len(mrb, name, &len);
+ str = mrb_sym_name_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);
+ iv_del(mrb, c->iv, MRB_SYM(__outer__), NULL);
+ iv_put(mrb, c->iv, MRB_SYM(__classname__), path);
mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
+ path = mrb_str_dup(mrb, path);
}
return path;
}
+
+size_t
+mrb_obj_iv_tbl_memsize(mrb_value obj)
+{
+ iv_tbl *t = mrb_obj_ptr(obj)->iv;
+ if (t == NULL) return 0;
+ return sizeof(iv_tbl) + t->alloc*sizeof(struct iv_elem);
+}
+
+#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
+
+mrb_bool
+mrb_ident_p(const char *s, mrb_int len)
+{
+ mrb_int i;
+
+ for (i = 0; i < len; i++) {
+ if (!identchar(s[i])) return FALSE;
+ }
+ return TRUE;
+}
diff --git a/src/vm.c b/src/vm.c
index 9eeb388fc..cd70faf11 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -6,7 +6,9 @@
#include <stddef.h>
#include <stdarg.h>
+#ifndef MRB_NO_FLOAT
#include <math.h>
+#endif
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
@@ -21,8 +23,10 @@
#include <mruby/opcode.h>
#include "value_array.h"
#include <mruby/throw.h>
+#include <mruby/dump.h>
+#include <mruby/presym.h>
-#ifdef MRB_DISABLE_STDIO
+#ifdef MRB_NO_STDIO
#if defined(__cplusplus)
extern "C" {
#endif
@@ -35,14 +39,6 @@ void abort(void);
#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
@@ -53,11 +49,6 @@ void abort(void);
#define MRB_FUNCALL_DEPTH_MAX 512
#endif
-/* Maximum depth of ecall() recursion. */
-#ifndef MRB_ECALL_DEPTH_MAX
-#define MRB_ECALL_DEPTH_MAX 32
-#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
@@ -100,26 +91,20 @@ void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value
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
+#ifdef MRB_NAN_BOXING
while (count-- > 0) {
SET_NIL_VALUE(*from);
from++;
}
+#else
+ memset(from, 0, sizeof(mrb_value)*count);
#endif
}
static inline void
stack_copy(mrb_value *dst, const mrb_value *src, size_t size)
{
- while (size-- > 0) {
- *dst++ = *src++;
- }
+ memcpy(dst, src, sizeof(mrb_value)*size);
}
static void
@@ -130,14 +115,13 @@ stack_init(mrb_state *mrb)
/* 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;
+ c->ci->u.target_class = mrb->object_class;
+ c->ci->stack = c->stbase;
}
static inline void
@@ -147,20 +131,20 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize
if (newbase == oldbase) return;
while (ci <= mrb->c->ci) {
- struct REnv *e = ci->env;
+ struct REnv *e = mrb_vm_ci_env(ci);
mrb_value *st;
- if (e && MRB_ENV_STACK_SHARED_P(e) &&
+ if (e && MRB_ENV_ONSTACK_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)) {
+ if (ci->proc && MRB_PROC_ENV_P(ci->proc) && e != MRB_PROC_ENV(ci->proc)) {
e = MRB_PROC_ENV(ci->proc);
- if (e && MRB_ENV_STACK_SHARED_P(e) &&
+ if (e && MRB_ENV_ONSTACK_P(e) &&
(st = e->stack) && oldbase <= st && st < oldbase+oldsize) {
ptrdiff_t off = e->stack - oldbase;
@@ -168,7 +152,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize
}
}
- ci->stackent = newbase + (ci->stackent - oldbase);
+ ci->stack = newbase + (ci->stack - oldbase);
ci++;
}
}
@@ -182,11 +166,11 @@ stack_extend_alloc(mrb_state *mrb, mrb_int room)
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;
+ size_t off = mrb->c->ci->stack ? mrb->c->stend - mrb->c->ci->stack : 0;
if (off > size) size = off;
#ifdef MRB_STACK_EXTEND_DOUBLING
- if (room <= (size_t)size)
+ if ((size_t)room <= size)
size *= 2;
else
size += room;
@@ -200,14 +184,13 @@ stack_extend_alloc(mrb_state *mrb, mrb_int room)
size += room;
#endif
- newstack = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size);
+ newstack = (mrb_value *)mrb_realloc_simple(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,
@@ -220,15 +203,15 @@ stack_extend_alloc(mrb_state *mrb, mrb_int room)
MRB_API void
mrb_stack_extend(mrb_state *mrb, mrb_int room)
{
- if (mrb->c->stack + room >= mrb->c->stend) {
+ if (!mrb->c->ci->stack || mrb->c->ci->stack + room >= mrb->c->stend) {
stack_extend_alloc(mrb, room);
}
}
static inline struct REnv*
-uvenv(mrb_state *mrb, int up)
+uvenv(mrb_state *mrb, mrb_int up)
{
- struct RProc *proc = mrb->c->ci->proc;
+ const struct RProc *proc = mrb->c->ci->proc;
struct REnv *e;
while (up--) {
@@ -243,7 +226,7 @@ uvenv(mrb_state *mrb, int up)
while (cb <= ci) {
if (ci->proc == proc) {
- return ci->env;
+ return mrb_vm_ci_env(ci);
}
ci--;
}
@@ -251,8 +234,8 @@ uvenv(mrb_state *mrb, int up)
return NULL;
}
-static inline struct RProc*
-top_proc(mrb_state *mrb, struct RProc *proc)
+static inline const struct RProc*
+top_proc(mrb_state *mrb, const struct RProc *proc)
{
while (proc->upper) {
if (MRB_PROC_SCOPE_P(proc) || MRB_PROC_STRICT_P(proc))
@@ -267,14 +250,12 @@ top_proc(mrb_state *mrb, struct RProc *proc)
#define CI_ACC_RESUMED -3
static inline mrb_callinfo*
-cipush(mrb_state *mrb)
+cipush(mrb_state *mrb, mrb_int push_stacks, mrb_int acc,
+ struct RClass *target_class, const struct RProc *proc, mrb_sym mid, mrb_int argc)
{
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;
@@ -283,9 +264,12 @@ cipush(mrb_state *mrb)
c->ciend = c->cibase + size * 2;
}
ci = ++c->ci;
- *ci = ci_zero;
- ci->epos = mrb->c->eidx;
- ci->ridx = ridx;
+ ci->mid = mid;
+ mrb_vm_ci_proc_set(ci, proc);
+ ci->stack = ci[-1].stack + push_stacks;
+ ci->argc = (int16_t)argc;
+ ci->acc = (int16_t)acc;
+ ci->u.target_class = target_class;
return ci;
}
@@ -295,82 +279,79 @@ mrb_env_unshare(mrb_state *mrb, struct REnv *e)
{
if (e == NULL) return;
else {
- size_t len = (size_t)MRB_ENV_STACK_LEN(e);
+ size_t len = (size_t)MRB_ENV_LEN(e);
mrb_value *p;
- if (!MRB_ENV_STACK_SHARED_P(e)) return;
+ if (!MRB_ENV_ONSTACK_P(e)) return;
if (e->cxt != mrb->c) return;
- if (e == mrb->c->cibase->env) return; /* for mirb */
+ if (e == mrb_vm_ci_env(mrb->c->cibase)) 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_ENV_CLOSE(e);
mrb_write_barrier(mrb, (struct RBasic *)e);
}
}
-static inline void
+static inline mrb_callinfo*
cipop(mrb_state *mrb)
{
struct mrb_context *c = mrb->c;
- struct REnv *env = c->ci->env;
+ struct REnv *env = mrb_vm_ci_env(c->ci);
c->ci--;
if (env) mrb_env_unshare(mrb, env);
+ return c->ci;
}
-void mrb_exc_set(mrb_state *mrb, mrb_value exc);
-
-static void
-ecall(mrb_state *mrb)
+MRB_API mrb_value
+mrb_protect_error(mrb_state *mrb, mrb_protect_error_func *body, void *userdata, mrb_bool *error)
{
- struct RProc *p;
- struct mrb_context *c = mrb->c;
- mrb_callinfo *ci = c->ci;
- struct RObject *exc;
- struct REnv *env;
- ptrdiff_t cioff;
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result = mrb_nil_value();
int ai = mrb_gc_arena_save(mrb);
- uint16_t i = --c->eidx;
- int nregs;
+ const struct mrb_context *c = mrb->c;
+ int ci_index = c->ci - c->cibase;
- if (i<0) return;
- if (ci - c->cibase > MRB_ECALL_DEPTH_MAX) {
- mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
+ if (error) { *error = FALSE; }
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, userdata);
+ mrb->jmp = prev_jmp;
}
- 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));
- }
- mrb_run(mrb, p, env->stack[0]);
- mrb->c = c;
- c->ci = c->cibase + cioff;
- if (!mrb->exc) mrb->exc = exc;
+ MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+ result = mrb_obj_value(mrb->exc);
+ mrb->exc = NULL;
+ if (error) { *error = TRUE; }
+ if (mrb->c == c) {
+ while (c->ci - c->cibase > ci_index) {
+ cipop(mrb);
+ }
+ }
+ else {
+ // It was probably switched by mrb_fiber_resume().
+ // Simply destroy all successive CI_ACC_DIRECTs once the fiber has been switched.
+ c = mrb->c;
+ while (c->ci > c->cibase && c->ci->acc == CI_ACC_DIRECT) {
+ cipop(mrb);
+ }
+ }
+ }
+ MRB_END_EXC(&c_jmp);
+
mrb_gc_arena_restore(mrb, ai);
+ mrb_gc_protect(mrb, result);
+ return result;
}
+void mrb_exc_set(mrb_state *mrb, mrb_value exc);
+static mrb_value mrb_run(mrb_state *mrb, const struct RProc* proc, mrb_value self);
+
#ifndef MRB_FUNCALL_ARGC_MAX
#define MRB_FUNCALL_ARGC_MAX 16
#endif
@@ -395,11 +376,30 @@ mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...)
return mrb_funcall_argv(mrb, self, mid, argc, argv);
}
-static int
+MRB_API mrb_value
+mrb_funcall_id(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, ...)
+{
+ mrb_value argv[MRB_FUNCALL_ARGC_MAX];
+ va_list ap;
+ mrb_int i;
+
+ 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 mrb_int
ci_nregs(mrb_callinfo *ci)
{
- struct RProc *p;
- int n = 0;
+ const struct RProc *p;
+ mrb_int n = 0;
if (!ci) return 3;
p = ci->proc;
@@ -423,6 +423,7 @@ 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;
+ int ai = mrb_gc_arena_save(mrb);
if (!mrb->jmp) {
struct mrb_jmpbuf c_jmp;
@@ -436,7 +437,6 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
}
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;
@@ -449,19 +449,19 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
mrb_method_t m;
struct RClass *c;
mrb_callinfo *ci;
- int n = ci_nregs(mrb->c->ci);
+ mrb_int n = ci_nregs(mrb->c->ci);
ptrdiff_t voff = -1;
- if (!mrb->c->stack) {
+ if (!mrb->c->stbase) {
stack_init(mrb);
}
if (argc < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%i)", 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_sym missing = MRB_SYM(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)) {
@@ -469,65 +469,53 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
}
mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
mrb_stack_extend(mrb, n+2);
- mrb->c->stack[n+1] = args;
+ mrb->c->ci->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;
+ ci = cipush(mrb, n, 0, c, NULL, mid, argc);
+ if (argc < 0) argc = 1;
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) {
+ 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;
+ mrb->c->ci->stack[1] = args;
ci->argc = -1;
argc = 1;
}
- else {
+ mrb_stack_extend(mrb, argc + 2);
+ if (MRB_METHOD_PROC_P(m)) {
struct RProc *p = MRB_METHOD_PROC(m);
- ci->proc = p;
- if (argc < 0) argc = 1;
- mrb_stack_extend(mrb, p->body.irep->nregs + argc);
+ mrb_vm_ci_proc_set(ci, p);
+ if (!MRB_PROC_CFUNC_P(p)) {
+ mrb_stack_extend(mrb, p->body.irep->nregs + argc);
+ }
}
if (voff >= 0) {
argv = mrb->c->stbase + voff;
}
- mrb->c->stack[0] = self;
+ mrb->c->ci->stack[0] = self;
if (ci->argc > 0) {
- stack_copy(mrb->c->stack+1, argv, argc);
+ stack_copy(mrb->c->ci->stack+1, argv, argc);
}
- mrb->c->stack[argc+1] = blk;
+ mrb->c->ci->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_arena_restore(mrb, ai);
mrb_gc_protect(mrb, val);
return val;
}
@@ -538,14 +526,14 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
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)
+static mrb_value
+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;
+ ci->stack[0] = self;
+ mrb_vm_ci_proc_set(ci, p);
if (MRB_PROC_CFUNC_P(p)) {
return MRB_PROC_CFUNC(p)(mrb, self);
}
@@ -557,18 +545,39 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
}
else {
mrb_stack_extend(mrb, nregs);
- stack_clear(mrb->c->stack+keep, nregs-keep);
+ stack_clear(ci->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;
+ cipush(mrb, 0, 0, NULL, NULL, 0, 0);
return self;
}
+mrb_value
+mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
+{
+ mrb_callinfo *ci = mrb->c->ci;
+ if (ci->acc >= 0) {
+ return exec_irep(mrb, self, p);
+ }
+ else {
+ mrb_value ret;
+ if (MRB_PROC_CFUNC_P(p)) {
+ cipush(mrb, 0, CI_ACC_DIRECT, mrb_vm_ci_target_class(ci), p, ci->mid, ci->argc);
+ ret = MRB_PROC_CFUNC(p)(mrb, self);
+ cipop(mrb);
+ }
+ else {
+ int keep = (ci->argc < 0 ? 1 : ci->argc) + 2 /* receiver + block */;
+ ret = mrb_top_run(mrb, p, self, keep);
+ }
+ if (mrb->exc && mrb->jmp) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
+ }
+ return ret;
+ }
+}
+
/* 15.3.1.3.4 */
/* 15.3.1.3.44 */
/*
@@ -588,11 +597,12 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
* k = Klass.new
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
*/
-MRB_API mrb_value
+mrb_value
mrb_f_send(mrb_state *mrb, mrb_value self)
{
mrb_sym name;
- mrb_value block, *argv, *regs;
+ mrb_value block, *regs;
+ const mrb_value *argv;
mrb_int argc, i, len;
mrb_method_t m;
struct RClass *c;
@@ -612,8 +622,8 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
}
ci->mid = name;
- ci->target_class = c;
- regs = mrb->c->stack+1;
+ ci->u.target_class = c;
+ regs = mrb->c->ci->stack+1;
/* remove first symbol from arguments */
if (ci->argc >= 0) {
for (i=0,len=ci->argc; i<len; i++) {
@@ -622,16 +632,16 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
ci->argc--;
}
else { /* variable length arguments */
- mrb_ary_shift(mrb, regs[0]);
+ regs[0] = mrb_ary_subseq(mrb, regs[0], 1, RARRAY_LEN(regs[0]) - 1);
}
if (MRB_METHOD_CFUNC_P(m)) {
if (MRB_METHOD_PROC_P(m)) {
- ci->proc = MRB_METHOD_PROC(m);
+ mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m));
}
return MRB_METHOD_CFUNC(m)(mrb, self);
}
- return mrb_exec_irep(mrb, self, MRB_METHOD_PROC(m));
+ return exec_irep(mrb, self, MRB_METHOD_PROC(m));
}
static mrb_value
@@ -646,31 +656,27 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
}
ci = mrb->c->ci;
if (ci->acc == CI_ACC_DIRECT) {
- ci->target_class = c;
- return mrb_yield_cont(mrb, blk, self, 1, &self);
+ return mrb_yield_with_class(mrb, blk, 1, &self, self, c);
}
- ci->target_class = c;
+ ci->u.target_class = c;
p = mrb_proc_ptr(blk);
- ci->proc = p;
+ mrb_vm_ci_proc_set(ci, 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();
+ mrb->c->ci->stack[0] = self;
+ mrb->c->ci->stack[1] = self;
+ mrb->c->ci->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;
+ if (nregs < 3) nregs = 3;
+ mrb_stack_extend(mrb, nregs);
+ mrb->c->ci->stack[0] = self;
+ mrb->c->ci->stack[1] = self;
+ stack_clear(mrb->c->ci->stack+2, nregs-2);
+ ci = cipush(mrb, 0, 0, NULL, NULL, 0, 0);
return self;
}
@@ -721,26 +727,11 @@ 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);
+ return eval_under(mrb, self, b, mrb_singleton_class_ptr(mrb, self));
}
MRB_API mrb_value
@@ -750,7 +741,7 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
mrb_sym mid = mrb->c->ci->mid;
mrb_callinfo *ci;
mrb_value val;
- int n;
+ mrb_int n;
if (mrb_nil_p(b)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
@@ -761,26 +752,29 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
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;
+ ci = cipush(mrb, n, CI_ACC_SKIP, c, p, mid, 0 /* dummy */);
+ if (argc >= CALL_MAXARGS) {
+ ci->argc = -1;
+ n = 3;
+ }
+ else {
+ ci->argc = (int)argc;
+ n = argc + 2;
+ }
mrb_stack_extend(mrb, n);
-
- mrb->c->stack[0] = self;
- if (argc > 0) {
- stack_copy(mrb->c->stack+1, argv, argc);
+ mrb->c->ci->stack[0] = self;
+ if (ci->argc < 0) {
+ mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
+ argc = 1;
+ }
+ else if (argc > 0) {
+ stack_copy(mrb->c->ci->stack+1, argv, argc);
}
- mrb->c->stack[argc+1] = mrb_nil_value();
+ mrb->c->ci->stack[argc+1] = mrb_nil_value();
if (MRB_PROC_CFUNC_P(p)) {
+ ci->acc = CI_ACC_DIRECT;
val = MRB_PROC_CFUNC(p)(mrb, self);
- mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
}
else {
@@ -814,7 +808,7 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const
if (mrb_nil_p(b)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
- if (mrb_type(b) != MRB_TT_PROC) {
+ if (!mrb_proc_p(b)) {
mrb_raise(mrb, E_TYPE_ERROR, "not a block");
}
@@ -822,47 +816,61 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const
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();
+ mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
+ mrb->c->ci->stack[2] = mrb_nil_value();
ci->argc = -1;
- return mrb_exec_irep(mrb, self, p);
+ return exec_irep(mrb, self, p);
}
-mrb_value
-mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod)
+static struct RBreak*
+break_new(mrb_state *mrb, uint32_t tag, const struct RProc *p, mrb_value val)
{
- struct RProc *proc;
- mrb_value ary;
- struct RClass *c = NULL;
+ struct RBreak *brk;
- 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);
+ brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL);
+ mrb_break_proc_set(brk, p);
+ mrb_break_value_set(brk, val);
+ mrb_break_tag_set(brk, tag);
- if (c2 != c) {
- c = c2;
- mrb_ary_push(mrb, ary, mrb_obj_value(c));
- }
- }
- proc = proc->upper;
- }
- return ary;
+ return brk;
}
-static struct RBreak*
-break_new(mrb_state *mrb, struct RProc *p, mrb_value val)
+#define MRB_CATCH_FILTER_RESCUE (UINT32_C(1) << MRB_CATCH_RESCUE)
+#define MRB_CATCH_FILTER_ENSURE (UINT32_C(1) << MRB_CATCH_ENSURE)
+#define MRB_CATCH_FILTER_ALL (MRB_CATCH_FILTER_RESCUE | MRB_CATCH_FILTER_ENSURE)
+
+static const struct mrb_irep_catch_handler *
+catch_handler_find(mrb_state *mrb, mrb_callinfo *ci, const mrb_code *pc, uint32_t filter)
{
- struct RBreak *brk;
+ const mrb_irep *irep;
+ ptrdiff_t xpc;
+ size_t cnt;
+ const struct mrb_irep_catch_handler *e;
+
+/* The comparison operators use `>` and `<=` because pc already points to the next instruction */
+#define catch_cover_p(pc, beg, end) ((pc) > (ptrdiff_t)(beg) && (pc) <= (ptrdiff_t)(end))
+
+ if (ci->proc == NULL || MRB_PROC_CFUNC_P(ci->proc)) return NULL;
+ irep = ci->proc->body.irep;
+ if (irep->clen < 1) return NULL;
+ xpc = pc - irep->iseq;
+ /* If it retry at the top level, pc will be 0, so check with -1 as the start position */
+ mrb_assert(catch_cover_p(xpc, -1, irep->ilen));
+ if (!catch_cover_p(xpc, -1, irep->ilen)) return NULL;
+
+ /* Currently uses a simple linear search to avoid processing complexity. */
+ cnt = irep->clen;
+ e = mrb_irep_catch_handler_table(irep) + cnt - 1;
+ for (; cnt > 0; cnt --, e --) {
+ if (((UINT32_C(1) << e->type) & filter) &&
+ catch_cover_p(xpc, mrb_irep_catch_handler_unpack(e->begin), mrb_irep_catch_handler_unpack(e->end))) {
+ return e;
+ }
+ }
- brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL);
- brk->proc = p;
- brk->val = val;
+#undef catch_cover_p
- return brk;
+ return NULL;
}
typedef enum {
@@ -895,27 +903,87 @@ argnum_error(mrb_state *mrb, mrb_int num)
mrb_int argc = mrb->c->ci->argc;
if (argc < 0) {
- mrb_value args = mrb->c->stack[1];
+ mrb_value args = mrb->c->ci->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));
+ str = mrb_format(mrb, "'%n': wrong number of arguments (%i for %i)",
+ mrb->c->ci->mid, argc, num);
}
else {
- str = mrb_format(mrb, "wrong number of arguments (%S for %S)",
- mrb_fixnum_value(argc), mrb_fixnum_value(num));
+ str = mrb_format(mrb, "wrong number of arguments (%i for %i)", argc, num);
}
exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str);
mrb_exc_set(mrb, exc);
}
-#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc;
-#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0;
-#ifdef MRB_ENABLE_DEBUG_HOOK
+static mrb_bool
+break_tag_p(struct RBreak *brk, uint32_t tag)
+{
+ return (brk != NULL && brk->tt == MRB_TT_BREAK) ? TRUE : FALSE;
+}
+
+static void
+prepare_tagged_break(mrb_state *mrb, uint32_t tag, const struct RProc *proc, mrb_value val)
+{
+ if (break_tag_p((struct RBreak*)mrb->exc, tag)) {
+ mrb_break_tag_set((struct RBreak*)mrb->exc, tag);
+ }
+ else {
+ mrb->exc = (struct RObject*)break_new(mrb, tag, proc, val);
+ }
+}
+
+#define THROW_TAGGED_BREAK(mrb, tag, proc, val) \
+ do { \
+ prepare_tagged_break(mrb, tag, proc, val); \
+ goto L_CATCH_TAGGED_BREAK; \
+ } while (0)
+
+#define UNWIND_ENSURE(mrb, ci, pc, tag, proc, val) \
+ do { \
+ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ENSURE); \
+ if (ch) { \
+ THROW_TAGGED_BREAK(mrb, tag, proc, val); \
+ } \
+ } while (0)
+
+/*
+ * CHECKPOINT_RESTORE(tag) {
+ * This part is executed when jumping by the same "tag" of RBreak (it is not executed the first time).
+ * Write the code required (initialization of variables, etc.) for the subsequent processing.
+ * }
+ * CHECKPOINT_MAIN(tag) {
+ * This part is always executed.
+ * }
+ * CHECKPOINT_END(tag);
+ *
+ * ...
+ *
+ * // Jump to CHECKPOINT_RESTORE with the same "tag".
+ * goto CHECKPOINT_LABEL_MAKE(tag);
+ */
+
+#define CHECKPOINT_LABEL_MAKE(tag) L_CHECKPOINT_ ## tag
+
+#define CHECKPOINT_RESTORE(tag) \
+ do { \
+ if (FALSE) { \
+ CHECKPOINT_LABEL_MAKE(tag): \
+ do {
+
+#define CHECKPOINT_MAIN(tag) \
+ } while (0); \
+ } \
+ do {
+
+#define CHECKPOINT_END(tag) \
+ } while (0); \
+ } while (0)
+
+#ifdef MRB_USE_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)
@@ -927,24 +995,24 @@ argnum_error(mrb_state *mrb, mrb_int num)
#define BYTECODE_DECODER(x) (x)
#endif
-#ifndef MRB_DISABLE_DIRECT_THREADING
+#ifndef MRB_NO_DIRECT_THREADING
#if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER
#define DIRECT_THREADED
#endif
-#endif /* ifndef MRB_DISABLE_DIRECT_THREADING */
+#endif /* ifndef MRB_NO_DIRECT_THREADING */
#ifndef DIRECT_THREADED
-#define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); pc++; CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) {
-#define CASE(insn,ops) case insn: pc++; FETCH_ ## ops ();; L_ ## insn ## _BODY:
-#define NEXT break
+#define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) {
+#define CASE(insn,ops) case insn: pc++; FETCH_ ## ops (); mrb->c->ci->pc = pc; L_ ## insn ## _BODY:
+#define NEXT goto L_END_DISPATCH
#define JUMP NEXT
-#define END_DISPATCH }}
+#define END_DISPATCH L_END_DISPATCH:;}}
#else
#define INIT_DISPATCH JUMP; return mrb_nil_value();
-#define CASE(insn,ops) L_ ## insn: pc++; FETCH_ ## ops (); L_ ## insn ## _BODY:
+#define CASE(insn,ops) L_ ## insn: pc++; FETCH_ ## ops (); mrb->c->ci->pc = pc; L_ ## insn ## _BODY:
#define NEXT insn=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[insn]
#define JUMP NEXT
@@ -953,65 +1021,89 @@ argnum_error(mrb_state *mrb, mrb_int num)
#endif
MRB_API mrb_value
-mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
+mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep)
{
- mrb_irep *irep = proc->body.irep;
+ const 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;
+ mrb_int nregs = irep->nregs;
- if (!c->stack) {
+ if (!c->stbase) {
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;
+ stack_clear(c->ci->stack + stack_keep, nregs - stack_keep);
+ c->ci->stack[0] = self;
result = mrb_vm_exec(mrb, proc, irep->iseq);
- if (c->ci - c->cibase > cioff) {
- c->ci = c->cibase + cioff;
- }
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");
+ if (!mrb_vm_ci_target_class(mrb->c->ci)) {
+ mrb_value exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR, "no target class or module");
mrb_exc_set(mrb, exc);
return FALSE;
}
return TRUE;
}
+mrb_value
+get_send_args(mrb_state *mrb, mrb_int argc, mrb_value *regs)
+{
+ if (argc < 0) return regs[0];
+ return mrb_ary_new_from_values(mrb, argc, regs);
+}
+
+static void
+proc_adjust_upper(struct RProc *p)
+{
+ /* skip upper procs while unnamed blocks and method closures */
+ while (p->upper) {
+ if (MRB_FLAG_TEST(p->upper, MRB_PROC_SCOPE) &&
+ !MRB_FLAG_TEST(p->upper, MRB_PROC_STRICT)) {
+ break;
+ }
+ p->upper = p->upper->upper;
+ }
+}
+
+mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod);
void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
+void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid);
MRB_API mrb_value
-mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
+mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *pc)
{
- /* mrb_assert(mrb_proc_cfunc_p(proc)) */
- mrb_irep *irep = proc->body.irep;
- mrb_value *pool = irep->pool;
- mrb_sym *syms = irep->syms;
+ /* mrb_assert(MRB_PROC_CFUNC_P(proc)) */
+ const mrb_irep *irep = proc->body.irep;
+ const mrb_pool_value *pool = irep->pool;
+ const 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;
+ uint16_t c;
+ mrb_sym mid;
+ const struct mrb_irep_catch_handler *ch;
#ifdef DIRECT_THREADED
- static void *optable[] = {
+ static const void * const optable[] = {
#define OPCODE(x,_) &&L_OP_ ## x,
#include "mruby/ops.h"
#undef OPCODE
@@ -1031,9 +1123,9 @@ RETRY_TRY_BLOCK:
goto L_RAISE;
}
mrb->jmp = &c_jmp;
- mrb->c->ci->proc = proc;
+ mrb_vm_ci_proc_set(mrb->c->ci, proc);
-#define regs (mrb->c->stack)
+#define regs (mrb->c->ci->stack)
INIT_DISPATCH {
CASE(OP_NOP, Z) {
/* do nothing */
@@ -1046,27 +1138,45 @@ RETRY_TRY_BLOCK:
}
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;
+ switch (pool[b].tt) { /* number */
+ case IREP_TT_INT32:
+ regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i32);
+ break;
+ case IREP_TT_INT64:
+#if defined(MRB_INT64)
+ regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i64);
+ break;
#else
- regs[a] = pool[b];
+#if defined(MRB_64BIT)
+ if (INT32_MIN <= pool[b].u.i64 && pool[b].u.i64 <= INT32_MAX) {
+ regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i64);
+ break;
+ }
#endif
+ goto L_INT_OVERFLOW;
+#endif
+ case IREP_TT_BIGINT:
+ goto L_INT_OVERFLOW;
+#ifndef MRB_NO_FLOAT
+ case IREP_TT_FLOAT:
+ regs[a] = mrb_float_value(mrb, pool[b].u.f);
+ break;
+#endif
+ default:
+ /* should not happen (tt:string) */
+ regs[a] = mrb_nil_value();
+ break;
+ }
NEXT;
}
CASE(OP_LOADI, BB) {
- SET_INT_VALUE(regs[a], b);
+ SET_FIXNUM_VALUE(regs[a], b);
NEXT;
}
CASE(OP_LOADINEG, BB) {
- SET_INT_VALUE(regs[a], -b);
+ SET_FIXNUM_VALUE(regs[a], -b);
NEXT;
}
@@ -1080,7 +1190,17 @@ RETRY_TRY_BLOCK:
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);
+ SET_FIXNUM_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0);
+ NEXT;
+ }
+
+ CASE(OP_LOADI16, BS) {
+ SET_FIXNUM_VALUE(regs[a], (mrb_int)(int16_t)b);
+ NEXT;
+ }
+
+ CASE(OP_LOADI32, BSS) {
+ SET_INT_VALUE(mrb, regs[a], (int32_t)(((uint32_t)b<<16)+c));
NEXT;
}
@@ -1121,13 +1241,13 @@ RETRY_TRY_BLOCK:
}
CASE(OP_GETSV, BB) {
- mrb_value val = mrb_vm_special_get(mrb, b);
+ mrb_value val = mrb_vm_special_get(mrb, syms[b]);
regs[a] = val;
NEXT;
}
CASE(OP_SETSV, BB) {
- mrb_vm_special_set(mrb, b, regs[a]);
+ mrb_vm_special_set(mrb, syms[b], regs[a]);
NEXT;
}
@@ -1143,9 +1263,7 @@ RETRY_TRY_BLOCK:
CASE(OP_GETCV, BB) {
mrb_value val;
- ERR_PC_SET(mrb, pc);
val = mrb_vm_cv_get(mrb, syms[b]);
- ERR_PC_CLR(mrb);
regs[a] = val;
NEXT;
}
@@ -1159,9 +1277,7 @@ RETRY_TRY_BLOCK:
mrb_value val;
mrb_sym sym = syms[b];
- ERR_PC_SET(mrb, pc);
val = mrb_vm_const_get(mrb, sym);
- ERR_PC_CLR(mrb);
regs[a] = val;
NEXT;
}
@@ -1174,9 +1290,7 @@ RETRY_TRY_BLOCK:
CASE(OP_GETMCNST, BB) {
mrb_value val;
- ERR_PC_SET(mrb, pc);
val = mrb_const_get(mrb, regs[a], syms[b]);
- ERR_PC_CLR(mrb);
regs[a] = val;
NEXT;
}
@@ -1190,7 +1304,7 @@ RETRY_TRY_BLOCK:
mrb_value *regs_a = regs + a;
struct REnv *e = uvenv(mrb, c);
- if (e && b < MRB_ENV_STACK_LEN(e)) {
+ if (e && b < MRB_ENV_LEN(e)) {
*regs_a = e->stack[b];
}
else {
@@ -1205,7 +1319,7 @@ RETRY_TRY_BLOCK:
if (e) {
mrb_value *regs_a = regs + a;
- if (b < MRB_ENV_STACK_LEN(e)) {
+ if (b < MRB_ENV_LEN(e)) {
e->stack[b] = *regs_a;
mrb_write_barrier(mrb, (struct RBasic*)e);
}
@@ -1214,57 +1328,75 @@ RETRY_TRY_BLOCK:
}
CASE(OP_JMP, S) {
- pc = irep->iseq+a;
+ pc += (int16_t)a;
JUMP;
}
CASE(OP_JMPIF, BS) {
if (mrb_test(regs[a])) {
- pc = irep->iseq+b;
+ pc += (int16_t)b;
JUMP;
}
NEXT;
}
CASE(OP_JMPNOT, BS) {
if (!mrb_test(regs[a])) {
- pc = irep->iseq+b;
+ pc += (int16_t)b;
JUMP;
}
NEXT;
}
CASE(OP_JMPNIL, BS) {
if (mrb_nil_p(regs[a])) {
- pc = irep->iseq+b;
+ pc += (int16_t)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;
+ CASE(OP_JMPUW, S) {
+ a = (uint32_t)((pc - irep->iseq) + (int16_t)a);
+ CHECKPOINT_RESTORE(RBREAK_TAG_JUMP) {
+ struct RBreak *brk = (struct RBreak*)mrb->exc;
+ mrb_value target = mrb_break_value_get(brk);
+ mrb_assert(mrb_integer_p(target));
+ a = (uint32_t)mrb_integer(target);
+ mrb_assert(a >= 0 && a < irep->ilen);
}
- /* 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;
+ CHECKPOINT_MAIN(RBREAK_TAG_JUMP) {
+ ch = catch_handler_find(mrb, mrb->c->ci, pc, MRB_CATCH_FILTER_ENSURE);
+ if (ch) {
+ /* avoiding a jump from a catch handler into the same handler */
+ if (a < mrb_irep_catch_handler_unpack(ch->begin) || a >= mrb_irep_catch_handler_unpack(ch->end)) {
+ THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, proc, mrb_fixnum_value(a));
}
}
- 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;
+ CHECKPOINT_END(RBREAK_TAG_JUMP);
+
+ mrb->exc = NULL; /* clear break object */
+ pc = irep->iseq + a;
+ JUMP;
}
CASE(OP_EXCEPT, B) {
- mrb_value exc = mrb_obj_value(mrb->exc);
- mrb->exc = 0;
+ mrb_value exc;
+
+ if (mrb->exc == NULL) {
+ exc = mrb_nil_value();
+ }
+ else {
+ switch (mrb->exc->tt) {
+ case MRB_TT_BREAK:
+ case MRB_TT_EXCEPTION:
+ exc = mrb_obj_value(mrb->exc);
+ break;
+ default:
+ mrb_assert(!"bad mrb_type");
+ exc = mrb_nil_value();
+ break;
+ }
+ mrb->exc = NULL;
+ }
regs[a] = exc;
NEXT;
}
@@ -1281,7 +1413,7 @@ RETRY_TRY_BLOCK:
{
mrb_value exc;
- exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR,
+ exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR,
"class or module required for rescue clause");
mrb_exc_set(mrb, exc);
goto L_RAISE;
@@ -1292,82 +1424,19 @@ RETRY_TRY_BLOCK:
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;
+ CASE(OP_RAISEIF, B) {
+ mrb_value exc = regs[a];
+ if (mrb_break_p(exc)) {
+ mrb->exc = mrb_obj_ptr(exc);
+ goto L_BREAK;
}
- /* 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);
+ mrb_exc_set(mrb, exc);
+ if (mrb->exc) {
+ goto L_RAISE;
}
- /* 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; n<a; n++) {
- int nregs = irep->nregs;
-
- 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;
@@ -1386,38 +1455,52 @@ RETRY_TRY_BLOCK:
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_int argc = (c == CALL_MAXARGS) ? -1 : c;
+ mrb_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_sym mid = syms[b];
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(),
+ if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) {
+ blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc));
+ /* The stack might have been reallocated during mrb_type_convert(),
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, pc);
+ mrb_sym missing = MRB_SYM(method_missing);
+ mrb_value args;
+
+ if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) {
+ method_missing:
+ args = get_send_args(mrb, argc, regs+a+1);
mrb_method_missing(mrb, mid, recv, args);
}
+ if (mid != missing) {
+ cls = mrb_class(mrb, recv);
+ }
+ m = mrb_method_search_vm(mrb, &cls, missing);
+ if (MRB_METHOD_UNDEF_P(m)) goto method_missing; /* just in case */
if (argc >= 0) {
if (a+2 >= irep->nregs) {
mrb_stack_extend(mrb, a+3);
@@ -1431,25 +1514,20 @@ RETRY_TRY_BLOCK:
}
/* 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;
+ ci = cipush(mrb, a, a, cls, NULL, mid, argc);
if (MRB_METHOD_CFUNC_P(m)) {
if (MRB_METHOD_PROC_P(m)) {
struct RProc *p = MRB_METHOD_PROC(m);
- ci->proc = p;
+ mrb_vm_ci_proc_set(ci, p);
recv = p->body.func(mrb, recv);
}
+ else if (MRB_METHOD_NOARG_P(m) &&
+ (argc > 0 || (argc == -1 && RARRAY_LEN(regs[1]) != 0))) {
+ argnum_error(mrb, 0);
+ goto L_RAISE;
+ }
else {
recv = MRB_METHOD_FUNC(m)(mrb, recv);
}
@@ -1457,13 +1535,13 @@ RETRY_TRY_BLOCK:
mrb_gc_arena_shrink(mrb, ai);
if (mrb->exc) goto L_RAISE;
ci = mrb->c->ci;
- if (mrb_type(blk) == MRB_TT_PROC) {
+ if (mrb_proc_p(blk)) {
struct RProc *p = mrb_proc_ptr(blk);
- if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) {
+ if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) {
p->flags |= MRB_PROC_ORPHAN;
}
}
- if (!ci->target_class) { /* return from context modifying method (resume/yield) */
+ if (!ci->u.target_class) { /* return from context modifying method (resume/yield) */
if (ci->acc == CI_ACC_RESUMED) {
mrb->jmp = prev_jmp;
return recv;
@@ -1476,43 +1554,34 @@ RETRY_TRY_BLOCK:
syms = irep->syms;
}
}
- mrb->c->stack[0] = recv;
+ mrb->c->ci->stack[0] = recv;
/* pop stackpos */
- mrb->c->stack = ci->stackent;
+ ci = cipop(mrb);
pc = ci->pc;
- cipop(mrb);
- JUMP;
}
else {
/* setup environment for calling method */
- proc = ci->proc = MRB_METHOD_PROC(m);
+ mrb_vm_ci_proc_set(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;
}
}
+ JUMP;
CASE(OP_CALL, Z) {
mrb_callinfo *ci;
- mrb_value recv = mrb->c->stack[0];
+ mrb_value recv = mrb->c->ci->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;
+ ci->u.target_class = MRB_PROC_TARGET_CLASS(m);
+ mrb_vm_ci_proc_set(ci, 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;
- }
+ ci->mid = MRB_PROC_ENV(m)->mid;
}
/* prepare stack */
@@ -1522,11 +1591,9 @@ RETRY_TRY_BLOCK:
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;
+ ci = cipop(mrb);
pc = ci->pc;
- cipop(mrb);
+ regs[ci[1].acc] = recv;
irep = mrb->c->ci->proc->body.irep;
pool = irep->pool;
syms = irep->syms;
@@ -1537,7 +1604,7 @@ RETRY_TRY_BLOCK:
proc = m;
irep = m->body.irep;
if (!irep) {
- mrb->c->stack[0] = mrb_nil_value();
+ mrb->c->ci->stack[0] = mrb_nil_value();
a = 0;
c = OP_R_NORMAL;
goto L_OP_RETURN_BODY;
@@ -1562,60 +1629,67 @@ RETRY_TRY_BLOCK:
}
CASE(OP_SUPER, BB) {
- int argc = (b == CALL_MAXARGS) ? -1 : b;
+ mrb_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;
+ const struct RProc *p = ci->proc;
mrb_sym mid = ci->mid;
- struct RClass* target_class = MRB_PROC_TARGET_CLASS(ci->proc);
+ struct RClass* target_class = MRB_PROC_TARGET_CLASS(p);
+ if (MRB_PROC_ENV_P(p) && p->e.env->mid && p->e.env->mid != mid) { /* alias support */
+ mid = p->e.env->mid; /* restore old mid */
+ }
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_value exc = mrb_exc_new_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->flags & MRB_FL_CLASS_IS_PREPENDED) {
+ target_class = mrb_vm_ci_target_class(ci);
+ }
+ else if (target_class->tt == MRB_TT_MODULE) {
+ target_class = mrb_vm_ci_target_class(ci);
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;
+ goto super_typeerror;
}
}
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,
+ super_typeerror: ;
+ mrb_value exc = mrb_exc_new_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");
+ if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) {
+ blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc));
/* The stack or ci stack might have been reallocated during
- mrb_convert_type(), see #3622 and #3784 */
+ mrb_type_convert(), 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");
+ mrb_sym missing = MRB_SYM(method_missing);
+ mrb_value args;
+ if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) {
+ super_missing:
+ args = get_send_args(mrb, argc, regs+a+1);
+ mrb_no_method_error(mrb, mid, args, "no superclass method '%n'", mid);
+ }
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, pc);
- mrb_method_missing(mrb, mid, recv, args);
- }
- mid = missing;
+ if (MRB_METHOD_UNDEF_P(m)) goto super_missing; /* just in case */
if (argc >= 0) {
if (a+2 >= irep->nregs) {
mrb_stack_extend(mrb, a+3);
@@ -1624,32 +1698,28 @@ RETRY_TRY_BLOCK:
regs[a+2] = blk;
argc = -1;
}
- mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
+ 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->pc = pc;
- ci->argc = argc;
+ ci = cipush(mrb, a, 0, cls, NULL, mid, argc);
/* prepare stack */
- mrb->c->stack += a;
- mrb->c->stack[0] = recv;
+ mrb->c->ci->stack[0] = recv;
if (MRB_METHOD_CFUNC_P(m)) {
mrb_value v;
if (MRB_METHOD_PROC_P(m)) {
- ci->proc = MRB_METHOD_PROC(m);
+ mrb_vm_ci_proc_set(ci, 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) */
+ mrb_assert(!mrb_break_p(v));
+ if (!mrb_vm_ci_target_class(ci)) { /* return from context modifying method (resume/yield) */
if (ci->acc == CI_ACC_RESUMED) {
mrb->jmp = prev_jmp;
return v;
@@ -1662,11 +1732,9 @@ RETRY_TRY_BLOCK:
syms = irep->syms;
}
}
- mrb->c->stack[0] = v;
- /* pop stackpos */
- mrb->c->stack = ci->stackent;
+ mrb->c->ci->stack[0] = v;
+ ci = cipop(mrb);
pc = ci->pc;
- cipop(mrb);
JUMP;
}
else {
@@ -1674,7 +1742,7 @@ RETRY_TRY_BLOCK:
ci->acc = a;
/* setup environment for calling method */
- proc = ci->proc = MRB_METHOD_PROC(m);
+ mrb_vm_ci_proc_set(ci, (proc = MRB_METHOD_PROC(m)));
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
@@ -1685,18 +1753,18 @@ RETRY_TRY_BLOCK:
}
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_int m1 = (b>>11)&0x3f;
+ mrb_int r = (b>>10)&0x1;
+ mrb_int m2 = (b>>5)&0x1f;
+ mrb_int kd = (b>>4)&0x1;
+ mrb_int lv = (b>>0)&0xf;
mrb_value *stack;
- if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) {
+ if (mrb->c->ci->mid == 0 || mrb_vm_ci_target_class(mrb->c->ci) == NULL) {
mrb_value exc;
L_NOSUPER:
- exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
+ exc = mrb_exc_new_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
@@ -1704,7 +1772,7 @@ RETRY_TRY_BLOCK:
else {
struct REnv *e = uvenv(mrb, lv-1);
if (!e) goto L_NOSUPER;
- if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+kd+1)
+ if (MRB_ENV_LEN(e) <= m1+r+m2+kd+1)
goto L_NOSUPER;
stack = e->stack + 1;
}
@@ -1714,13 +1782,13 @@ RETRY_TRY_BLOCK:
else {
mrb_value *pp = NULL;
struct RArray *rest;
- int len = 0;
+ mrb_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);
+ len = ARY_LEN(ary);
}
regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd);
rest = mrb_ary_ptr(regs[a]);
@@ -1744,22 +1812,22 @@ RETRY_TRY_BLOCK:
}
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;
+ mrb_int m1 = MRB_ASPEC_REQ(a);
+ mrb_int o = MRB_ASPEC_OPT(a);
+ mrb_int r = MRB_ASPEC_REST(a);
+ mrb_int m2 = MRB_ASPEC_POST(a);
+ mrb_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_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_int const len = m1 + o + r + m2;
+ mrb_int const blk_pos = len + kd + 1;
mrb_value *blk = &argv[argc < 0 ? 1 : argc];
- mrb_value kdict;
- int kargs = kd;
+ mrb_value kdict = mrb_nil_value();
+ mrb_int kargs = kd;
/* arguments is passed with Array */
if (argc < 0) {
@@ -1771,11 +1839,9 @@ RETRY_TRY_BLOCK:
/* strict argument check */
if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
- if (argc >= 0 && !(argc <= 1 && kd)) {
- if (argc < m1 + m2 + kd || (r == 0 && argc > len + kd)) {
- argnum_error(mrb, m1+m2);
- goto L_RAISE;
- }
+ if (argc < m1 + m2 || (r == 0 && argc > len + kd)) {
+ argnum_error(mrb, m1+m2);
+ goto L_RAISE;
}
}
/* extract first argument array to arguments */
@@ -1796,13 +1862,13 @@ RETRY_TRY_BLOCK:
kdict = argv[argc-1];
mrb_hash_check_kdict(mrb, kdict);
}
- else if (r) {
+ else if (r || argc <= m1+m2+o
+ || !(mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc))) {
kdict = mrb_hash_new(mrb);
kargs = 0;
}
else {
- mrb_value str = mrb_str_new_lit(mrb, "Excepcted `Hash` as last argument for keyword arguments");
- mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
+ argnum_error(mrb, m1+m2);
goto L_RAISE;
}
if (MRB_ASPEC_KEY(a) > 0) {
@@ -1813,7 +1879,7 @@ RETRY_TRY_BLOCK:
/* no rest arguments */
if (argc-kargs < len) {
- int mlen = m2;
+ mrb_int mlen = m2;
if (argc < m1+m2) {
mlen = m1 < argc ? argc - m1 : 0;
}
@@ -1821,7 +1887,7 @@ RETRY_TRY_BLOCK:
if (kd) regs[len + 1] = kdict;
/* copy mandatory and optional arguments */
- if (argv0 != argv) {
+ if (argv0 != argv && argv) {
value_move(&regs[1], argv, argc-mlen); /* m1 + o */
}
if (argc < m1) {
@@ -1834,16 +1900,16 @@ RETRY_TRY_BLOCK:
if (mlen < m2) {
stack_clear(&regs[len-m2+mlen+1], m2-mlen);
}
- /* initalize rest arguments with empty Array */
+ /* initialize rest arguments with empty Array */
if (r) {
regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
}
- /* skip initailizer of passed arguments */
+ /* skip initializer of passed arguments */
if (o > 0 && argc-kargs > m1+m2)
pc += (argc - kargs - m1 - m2)*3;
}
else {
- int rnum = 0;
+ mrb_int rnum = 0;
if (argv0 != argv) {
regs[blk_pos] = *blk; /* move block */
if (kd) regs[len + 1] = kdict;
@@ -1869,7 +1935,7 @@ RETRY_TRY_BLOCK:
}
/* format arguments for generated code */
- mrb->c->ci->argc = len + kd;
+ mrb->c->ci->argc = (int16_t)(len + kd);
/* clear local (but non-argument) variables */
if (irep->nlocals-blk_pos-1 > 0) {
@@ -1882,8 +1948,8 @@ RETRY_TRY_BLOCK:
mrb_value k = mrb_symbol_value(syms[b]);
mrb_value kdict = regs[mrb->c->ci->argc];
- if (!mrb_hash_key_p(mrb, kdict, k)) {
- mrb_value str = mrb_format(mrb, "missing keyword: %S", k);
+ if (!mrb_hash_p(kdict) || !mrb_hash_key_p(mrb, kdict, k)) {
+ mrb_value str = mrb_format(mrb, "missing keyword: %v", k);
mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
goto L_RAISE;
}
@@ -1895,8 +1961,11 @@ RETRY_TRY_BLOCK:
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 = mrb_hash_key_p(mrb, kdict, k);
+ 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;
}
@@ -1907,7 +1976,7 @@ RETRY_TRY_BLOCK:
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_value str = mrb_format(mrb, "unknown keyword: %v", key1);
mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
goto L_RAISE;
}
@@ -1926,13 +1995,7 @@ RETRY_TRY_BLOCK:
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)
+ mrb_callinfo *ci;
ci = mrb->c->ci;
if (ci->mid) {
@@ -1944,50 +2007,43 @@ RETRY_TRY_BLOCK:
else {
blk = regs[ci->argc+1];
}
- if (mrb_type(blk) == MRB_TT_PROC) {
+ if (mrb_proc_p(blk)) {
struct RProc *p = mrb_proc_ptr(blk);
if (!MRB_PROC_STRICT_P(p) &&
- ci > mrb->c->cibase && MRB_PROC_ENV(p) == ci[-1].env) {
+ ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) {
p->flags |= MRB_PROC_ORPHAN;
}
}
}
if (mrb->exc) {
- mrb_callinfo *ci0;
-
L_RAISE:
- ci0 = ci = mrb->c->ci;
+ ci = mrb->c->ci;
if (ci == mrb->c->cibase) {
- if (ci->ridx == 0) goto L_FTOP;
- goto L_RESCUE;
+ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL);
+ if (ch == NULL) goto L_FTOP;
+ goto L_CATCH;
}
- while (ci[0].ridx == ci[-1].ridx) {
- cipop(mrb);
- mrb->c->stack = ci->stackent;
- if (ci->acc == CI_ACC_SKIP && prev_jmp) {
+ while ((ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL)) == NULL) {
+ ci = cipop(mrb);
+ if (ci[1].acc == CI_ACC_SKIP && prev_jmp) {
mrb->jmp = prev_jmp;
MRB_THROW(prev_jmp);
}
- ci = mrb->c->ci;
+ pc = ci[0].pc;
if (ci == mrb->c->cibase) {
- if (ci->ridx == 0) {
+ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL);
+ if (ch == NULL) {
L_FTOP: /* fiber top */
if (mrb->c == mrb->root_c) {
- mrb->c->stack = mrb->c->stbase;
+ mrb->c->ci->stack = mrb->c->stbase;
goto L_STOP;
}
else {
struct mrb_context *c = mrb->c;
- while (c->eidx > ci->epos) {
- ecall_adjust();
- }
- if (c->fib) {
- mrb_write_barrier(mrb, (struct RBasic*)c->fib);
- }
- mrb->c->status = MRB_FIBER_TERMINATED;
+ c->status = MRB_FIBER_TERMINATED;
mrb->c = c->prev;
c->prev = NULL;
goto L_RAISE;
@@ -1995,29 +2051,23 @@ RETRY_TRY_BLOCK:
}
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;
+ L_CATCH:
+ if (ch == NULL) goto L_STOP;
+ if (FALSE) {
+ L_CATCH_TAGGED_BREAK: /* from THROW_TAGGED_BREAK() or UNWIND_ENSURE() */
+ ci = mrb->c->ci;
+ }
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];
+ pc = irep->iseq + mrb_irep_catch_handler_unpack(ch->target);
}
else {
- int acc;
+ mrb_int acc;
mrb_value v;
- struct RProc *dst;
ci = mrb->c->ci;
v = regs[a];
@@ -2026,55 +2076,92 @@ RETRY_TRY_BLOCK:
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;
+ const struct RProc *dst;
+ mrb_callinfo *cibase;
+ 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) {
+ if (!MRB_ENV_ONSTACK_P(e) || (e->cxt && e->cxt != mrb->c)) {
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
}
+ /* check jump destination */
while (cibase <= ci && ci->proc != dst) {
- if (ci->acc < 0) {
+ if (ci->acc < 0) { /* jump cross C boundary */
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
ci--;
}
- if (ci <= cibase) {
+ if (ci <= cibase) { /* no jump destination */
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
+ ci = mrb->c->ci;
+ while (cibase <= ci && ci->proc != dst) {
+ CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_BLOCK) {
+ cibase = mrb->c->cibase;
+ dst = top_proc(mrb, proc);
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_RETURN_BLOCK) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_BLOCK, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_RETURN_BLOCK);
+ ci = cipop(mrb);
+ pc = ci->pc;
+ }
+ proc = ci->proc;
+ mrb->exc = NULL; /* clear break object */
break;
}
/* fallthrough */
case OP_R_NORMAL:
NORMAL_RETURN:
if (ci == mrb->c->cibase) {
- struct mrb_context *c = mrb->c;
+ struct mrb_context *c;
+ c = mrb->c;
if (!c->prev) { /* toplevel return */
regs[irep->nlocals] = v;
- goto L_STOP;
+ goto CHECKPOINT_LABEL_MAKE(RBREAK_TAG_STOP);
}
- if (c->prev->ci == c->prev->cibase) {
- mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume");
+ if (!c->vmexec && c->prev->ci == c->prev->cibase) {
+ mrb_value exc = mrb_exc_new_lit(mrb, E_FIBER_ERROR, "double resume");
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
- while (c->eidx > 0) {
- ecall(mrb);
+ CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_TOPLEVEL) {
+ c = mrb->c;
}
+ CHECKPOINT_MAIN(RBREAK_TAG_RETURN_TOPLEVEL) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_TOPLEVEL, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_RETURN_TOPLEVEL);
/* automatic yield at the end */
c->status = MRB_FIBER_TERMINATED;
mrb->c = c->prev;
- c->prev = NULL;
mrb->c->status = MRB_FIBER_RUNNING;
+ c->prev = NULL;
+ if (c->vmexec) {
+ mrb_gc_arena_restore(mrb, ai);
+ c->vmexec = FALSE;
+ mrb->jmp = prev_jmp;
+ return v;
+ }
ci = mrb->c->ci;
}
+ CHECKPOINT_RESTORE(RBREAK_TAG_RETURN) {
+ /* do nothing */
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_RETURN) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_RETURN);
+ mrb->exc = NULL; /* clear break object */
break;
case OP_R_BREAK:
if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN;
@@ -2082,12 +2169,12 @@ RETRY_TRY_BLOCK:
mrb_value exc;
L_BREAK_ERROR:
- exc = mrb_exc_new_str_lit(mrb, E_LOCALJUMP_ERROR,
+ exc = mrb_exc_new_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))) {
+ if (!MRB_PROC_ENV_P(proc) || !MRB_ENV_ONSTACK_P(MRB_PROC_ENV(proc))) {
goto L_BREAK_ERROR;
}
else {
@@ -2097,9 +2184,13 @@ RETRY_TRY_BLOCK:
goto L_BREAK_ERROR;
}
}
- while (mrb->c->eidx > mrb->c->ci->epos) {
- ecall_adjust();
+ CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) {
+ /* do nothing */
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_BREAK) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK, proc, v);
}
+ CHECKPOINT_END(RBREAK_TAG_BREAK);
/* break from fiber block */
if (ci == mrb->c->cibase && ci->pc) {
struct mrb_context *c = mrb->c;
@@ -2109,62 +2200,78 @@ RETRY_TRY_BLOCK:
ci = mrb->c->ci;
}
if (ci->acc < 0) {
+ ci = cipop(mrb);
mrb_gc_arena_restore(mrb, ai);
mrb->c->vmexec = FALSE;
- mrb->exc = (struct RObject*)break_new(mrb, proc, v);
+ mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, proc, v);
mrb->jmp = prev_jmp;
MRB_THROW(prev_jmp);
}
if (FALSE) {
+ struct RBreak *brk;
+
L_BREAK:
- v = ((struct RBreak*)mrb->exc)->val;
- proc = ((struct RBreak*)mrb->exc)->proc;
- mrb->exc = NULL;
+ brk = (struct RBreak*)mrb->exc;
+ proc = mrb_break_proc_get(brk);
+ v = mrb_break_value_get(brk);
ci = mrb->c->ci;
+
+ switch (mrb_break_tag_get(brk)) {
+#define DISPATCH_CHECKPOINTS(n, i) case n: goto CHECKPOINT_LABEL_MAKE(n);
+ RBREAK_TAG_FOREACH(DISPATCH_CHECKPOINTS)
+#undef DISPATCH_CHECKPOINTS
+ default:
+ mrb_assert(!"wrong break tag");
+ }
}
- mrb->c->stack = ci->stackent;
- proc = proc->upper;
- while (mrb->c->cibase < ci && ci[-1].proc != proc) {
+ while (mrb->c->cibase < ci && ci[-1].proc != proc->upper) {
if (ci[-1].acc == CI_ACC_SKIP) {
- while (ci < mrb->c->ci) {
- cipop(mrb);
- }
goto L_BREAK_ERROR;
}
- ci--;
+ CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_UPPER) {
+ /* do nothing */
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_BREAK_UPPER) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_UPPER, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_BREAK_UPPER);
+ ci = cipop(mrb);
+ pc = ci->pc;
}
+ CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_INTARGET) {
+ /* do nothing */
+ }
+ CHECKPOINT_MAIN(RBREAK_TAG_BREAK_INTARGET) {
+ UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_INTARGET, proc, v);
+ }
+ CHECKPOINT_END(RBREAK_TAG_BREAK_INTARGET);
if (ci == mrb->c->cibase) {
goto L_BREAK_ERROR;
}
+ mrb->exc = NULL; /* clear break object */
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_assert(ci == mrb->c->ci);
+ mrb_assert(mrb->exc == NULL);
+
+ if (mrb->c->vmexec && !mrb_vm_ci_target_class(ci)) {
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);
+ ci = 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)));
+ pc = ci[0].pc;
+ DEBUG(fprintf(stderr, "from :%s\n", mrb_sym_name(mrb, ci->mid)));
proc = mrb->c->ci->proc;
irep = proc->body.irep;
pool = irep->pool;
@@ -2187,8 +2294,8 @@ RETRY_TRY_BLOCK:
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) {
+ if (!e || (!MRB_ENV_ONSTACK_P(e) && e->mid == 0) ||
+ MRB_ENV_LEN(e) <= m1+r+m2+1) {
localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
goto L_RAISE;
}
@@ -2202,210 +2309,98 @@ RETRY_TRY_BLOCK:
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, BB) {
- /* 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;
- goto L_SEND;
- }
- mrb_gc_arena_restore(mrb, ai);
- NEXT;
+ L_INT_OVERFLOW:
+ {
+ mrb_value exc = mrb_exc_new_lit(mrb, E_RANGE_ERROR, "integer overflow");
+ mrb_exc_set(mrb, exc);
}
+ goto L_RAISE;
- CASE(OP_SUB, BB) {
- /* 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);
- }
+#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff))
+#define OP_MATH(op_name) \
+ /* need to check if op is overridden */ \
+ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { \
+ OP_MATH_CASE_INTEGER(op_name); \
+ OP_MATH_CASE_FLOAT(op_name, integer, float); \
+ OP_MATH_CASE_FLOAT(op_name, float, integer); \
+ OP_MATH_CASE_FLOAT(op_name, float, float); \
+ OP_MATH_CASE_STRING_##op_name(); \
+ default: \
+ c = 1; \
+ mid = MRB_OPSYM(op_name); \
+ goto L_SEND_SYM; \
+ } \
+ NEXT;
+#define OP_MATH_CASE_INTEGER(op_name) \
+ case TYPES2(MRB_TT_INTEGER, MRB_TT_INTEGER): \
+ { \
+ mrb_int x = mrb_integer(regs[a]), y = mrb_integer(regs[a+1]), z; \
+ if (mrb_int_##op_name##_overflow(x, y, &z)) \
+ OP_MATH_OVERFLOW_INT(); \
+ else \
+ SET_INT_VALUE(mrb,regs[a], z); \
+ } \
+ break
+#ifdef MRB_NO_FLOAT
+#define OP_MATH_CASE_FLOAT(op_name, t1, t2) (void)0
#else
- OP_MATH_BODY(-,mrb_float,mrb_float);
+#define OP_MATH_CASE_FLOAT(op_name, t1, t2) \
+ case TYPES2(OP_MATH_TT_##t1, OP_MATH_TT_##t2): \
+ { \
+ mrb_float z = mrb_##t1(regs[a]) OP_MATH_OP_##op_name mrb_##t2(regs[a+1]); \
+ SET_FLOAT_VALUE(mrb, regs[a], z); \
+ } \
+ break
#endif
- break;
-#endif
- default:
- c = 1;
- goto L_SEND;
- }
- NEXT;
+#define OP_MATH_OVERFLOW_INT() goto L_INT_OVERFLOW
+#define OP_MATH_CASE_STRING_add() \
+ case TYPES2(MRB_TT_STRING, MRB_TT_STRING): \
+ regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); \
+ mrb_gc_arena_restore(mrb, ai); \
+ break
+#define OP_MATH_CASE_STRING_sub() (void)0
+#define OP_MATH_CASE_STRING_mul() (void)0
+#define OP_MATH_OP_add +
+#define OP_MATH_OP_sub -
+#define OP_MATH_OP_mul *
+#define OP_MATH_TT_integer MRB_TT_INTEGER
+#define OP_MATH_TT_float MRB_TT_FLOAT
+
+ CASE(OP_ADD, B) {
+ OP_MATH(add);
}
- CASE(OP_MUL, BB) {
- /* 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;
+ CASE(OP_SUB, B) {
+ OP_MATH(sub);
+ }
- 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;
- goto L_SEND;
- }
- NEXT;
+ CASE(OP_MUL, B) {
+ OP_MATH(mul);
}
- CASE(OP_DIV, BB) {
-#ifndef MRB_WITHOUT_FLOAT
- double x, y, f;
+ CASE(OP_DIV, B) {
+#ifndef MRB_NO_FLOAT
+ mrb_float 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
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):
{
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_INT_VALUE(regs[a], y ? x / y : 0);
+ mrb_int x = mrb_integer(regs[a]);
+ mrb_int y = mrb_integer(regs[a+1]);
+ mrb_int div = mrb_div_int(mrb, x, y);
+ SET_INT_VALUE(mrb, regs[a], div);
}
- 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]);
+ NEXT;
+#ifndef MRB_NO_FLOAT
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT):
+ x = (mrb_float)mrb_integer(regs[a]);
y = mrb_float(regs[a+1]);
break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
+ case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER):
x = mrb_float(regs[a]);
- y = (mrb_float)mrb_fixnum(regs[a+1]);
+ y = (mrb_float)mrb_integer(regs[a+1]);
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
x = mrb_float(regs[a]);
@@ -2414,114 +2409,73 @@ RETRY_TRY_BLOCK:
#endif
default:
c = 1;
- goto L_SEND;
+ mid = MRB_OPSYM(div);
+ 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;
- }
+#ifndef MRB_NO_FLOAT
+ f = mrb_div_float(x, y);
SET_FLOAT_VALUE(mrb, regs[a], f);
#endif
NEXT;
}
- CASE(OP_ADDI, BBB) {
- /* 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)c;
- 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 + c);
- }
+#define OP_MATHI(op_name) \
+ /* need to check if op is overridden */ \
+ switch (mrb_type(regs[a])) { \
+ OP_MATHI_CASE_INTEGER(op_name); \
+ OP_MATHI_CASE_FLOAT(op_name); \
+ default: \
+ SET_INT_VALUE(mrb,regs[a+1], b); \
+ c = 1; \
+ mid = MRB_OPSYM(op_name); \
+ goto L_SEND_SYM; \
+ } \
+ NEXT;
+#define OP_MATHI_CASE_INTEGER(op_name) \
+ case MRB_TT_INTEGER: \
+ { \
+ mrb_int x = mrb_integer(regs[a]), y = (mrb_int)b, z; \
+ if (mrb_int_##op_name##_overflow(x, y, &z)) \
+ OP_MATH_OVERFLOW_INT(); \
+ else \
+ SET_INT_VALUE(mrb,regs[a], z); \
+ } \
+ break
+#ifdef MRB_NO_FLOAT
+#define OP_MATHI_CASE_FLOAT(op_name) (void)0
#else
- mrb_float(regs[a]) += c;
+#define OP_MATHI_CASE_FLOAT(op_name) \
+ case MRB_TT_FLOAT: \
+ { \
+ mrb_float z = mrb_float(regs[a]) OP_MATH_OP_##op_name b; \
+ SET_FLOAT_VALUE(mrb, regs[a], z); \
+ } \
+ break
#endif
- break;
-#endif
- default:
- SET_INT_VALUE(regs[a+1], c);
- c = 1;
- goto L_SEND;
- }
- NEXT;
- }
- CASE(OP_SUBI, BBB) {
- 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)c;
- mrb_int z;
+ CASE(OP_ADDI, BB) {
+ OP_MATHI(add);
+ }
- 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)c);
- }
-#else
- mrb_float(regs_a[0]) -= c;
-#endif
- break;
-#endif
- default:
- SET_INT_VALUE(regs_a[1], c);
- c = 1;
- goto L_SEND;
- }
- NEXT;
+ CASE(OP_SUBI, BB) {
+ OP_MATHI(sub);
}
#define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1]))
-#ifdef MRB_WITHOUT_FLOAT
-#define OP_CMP(op) do {\
+#ifdef MRB_NO_FLOAT
+#define OP_CMP(op,sym) 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):\
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\
result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
break;\
default:\
c = 1;\
- goto L_SEND;\
+ mid = MRB_OPSYM(sym);\
+ goto L_SEND_SYM;\
}\
if (result) {\
SET_TRUE_VALUE(regs[a]);\
@@ -2531,17 +2485,17 @@ RETRY_TRY_BLOCK:
}\
} while(0)
#else
-#define OP_CMP(op) do {\
+#define OP_CMP(op, sym) 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):\
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\
result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
break;\
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\
+ case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT):\
result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\
break;\
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\
+ case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER):\
result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\
break;\
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\
@@ -2549,7 +2503,8 @@ RETRY_TRY_BLOCK:
break;\
default:\
c = 1;\
- goto L_SEND;\
+ mid = MRB_OPSYM(sym);\
+ goto L_SEND_SYM;\
}\
if (result) {\
SET_TRUE_VALUE(regs[a]);\
@@ -2560,33 +2515,33 @@ RETRY_TRY_BLOCK:
} while(0)
#endif
- CASE(OP_EQ, BB) {
+ CASE(OP_EQ, B) {
if (mrb_obj_eq(mrb, regs[a], regs[a+1])) {
SET_TRUE_VALUE(regs[a]);
}
else {
- OP_CMP(==);
+ OP_CMP(==,eq);
}
NEXT;
}
- CASE(OP_LT, BB) {
- OP_CMP(<);
+ CASE(OP_LT, B) {
+ OP_CMP(<,lt);
NEXT;
}
- CASE(OP_LE, BB) {
- OP_CMP(<=);
+ CASE(OP_LE, B) {
+ OP_CMP(<=,le);
NEXT;
}
- CASE(OP_GT, BB) {
- OP_CMP(>);
+ CASE(OP_GT, B) {
+ OP_CMP(>,gt);
NEXT;
}
- CASE(OP_GE, BB) {
- OP_CMP(>=);
+ CASE(OP_GE, B) {
+ OP_CMP(>=,ge);
NEXT;
}
@@ -2605,7 +2560,12 @@ RETRY_TRY_BLOCK:
CASE(OP_ARYCAT, B) {
mrb_value splat = mrb_ary_splat(mrb, regs[a+1]);
- mrb_ary_concat(mrb, regs[a], splat);
+ if (mrb_nil_p(regs[a])) {
+ regs[a] = splat;
+ }
+ else {
+ mrb_ary_concat(mrb, regs[a], splat);
+ }
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
@@ -2693,9 +2653,15 @@ RETRY_TRY_BLOCK:
}
CASE(OP_STRING, BB) {
- mrb_value str = mrb_str_dup(mrb, pool[b]);
+ size_t len;
- regs[a] = str;
+ len = pool[b].tt >> 2;
+ if (pool[b].tt & IREP_TT_SFLAG) {
+ regs[a] = mrb_str_new_static(mrb, pool[b].u.str, len);
+ }
+ else {
+ regs[a] = mrb_str_new(mrb, pool[b].u.str, len);
+ }
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
@@ -2743,7 +2709,7 @@ RETRY_TRY_BLOCK:
L_MAKE_LAMBDA:
{
struct RProc *p;
- mrb_irep *nirep = irep->reps[b];
+ const mrb_irep *nirep = irep->reps[b];
if (c & OP_L_CAPTURE) {
p = mrb_closure_new(mrb, nirep);
@@ -2753,6 +2719,7 @@ RETRY_TRY_BLOCK:
p->flags |= MRB_PROC_SCOPE;
}
if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT;
+ if (c == OP_L_METHOD) proc_adjust_upper(p);
regs[a] = mrb_obj_value(p);
mrb_gc_arena_restore(mrb, ai);
NEXT;
@@ -2794,6 +2761,7 @@ RETRY_TRY_BLOCK:
super = regs[a+1];
if (mrb_nil_p(base)) {
baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ if (!baseclass) baseclass = mrb->object_class;
base = mrb_obj_value(baseclass);
}
c = mrb_vm_define_class(mrb, base, super, id);
@@ -2810,6 +2778,7 @@ RETRY_TRY_BLOCK:
base = regs[a];
if (mrb_nil_p(base)) {
baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+ if (!baseclass) baseclass = mrb->object_class;
base = mrb_obj_value(baseclass);
}
cls = mrb_vm_define_module(mrb, base, id);
@@ -2818,11 +2787,11 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_EXEC, BB) {
- mrb_callinfo *ci;
+ CASE(OP_EXEC, BB)
+ {
mrb_value recv = regs[a];
struct RProc *p;
- mrb_irep *nirep = irep->reps[b];
+ const mrb_irep *nirep = irep->reps[b];
/* prepare closure */
p = mrb_proc_new(mrb, nirep);
@@ -2830,21 +2799,10 @@ RETRY_TRY_BLOCK:
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;
+ proc_adjust_upper(p);
/* 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;
+ cipush(mrb, a, a, mrb_class_ptr(recv), p, 0, 0);
irep = p->body.irep;
pool = irep->pool;
@@ -2859,10 +2817,13 @@ RETRY_TRY_BLOCK:
struct RClass *target = mrb_class_ptr(regs[a]);
struct RProc *p = mrb_proc_ptr(regs[a+1]);
mrb_method_t m;
+ mrb_sym mid = syms[b];
MRB_METHOD_FROM_PROC(m, p);
- mrb_define_method_raw(mrb, target, syms[b], m);
+ mrb_define_method_raw(mrb, target, mid, m);
+ mrb_method_added(mrb, target, mid);
mrb_gc_arena_restore(mrb, ai);
+ regs[a] = mrb_symbol_value(mid);
NEXT;
}
@@ -2874,7 +2835,7 @@ RETRY_TRY_BLOCK:
CASE(OP_TCLASS, B) {
if (!check_target_class(mrb)) goto L_RAISE;
- regs[a] = mrb_obj_value(mrb->c->ci->target_class);
+ regs[a] = mrb_obj_value(mrb_vm_ci_target_class(mrb->c->ci));
NEXT;
}
@@ -2882,25 +2843,26 @@ RETRY_TRY_BLOCK:
struct RClass *target;
if (!check_target_class(mrb)) goto L_RAISE;
- target = mrb->c->ci->target_class;
+ target = mrb_vm_ci_target_class(mrb->c->ci);
mrb_alias_method(mrb, target, syms[a], syms[b]);
- NEXT;
+ mrb_method_added(mrb, target, syms[a]);
+ NEXT;
}
CASE(OP_UNDEF, B) {
struct RClass *target;
if (!check_target_class(mrb)) goto L_RAISE;
- target = mrb->c->ci->target_class;
+ target = mrb_vm_ci_target_class(mrb->c->ci);
mrb_undef_method_id(mrb, target, syms[a]);
NEXT;
}
CASE(OP_DEBUG, Z) {
FETCH_BBB();
-#ifdef MRB_ENABLE_DEBUG_HOOK
+#ifdef MRB_USE_DEBUG_HOOK
mrb->debug_op_hook(mrb, irep, pc, regs);
#else
-#ifndef MRB_DISABLE_STDIO
+#ifndef MRB_NO_STDIO
printf("OP_DEBUG %d %d %d\n", a, b, c);
#else
abort();
@@ -2910,19 +2872,23 @@ RETRY_TRY_BLOCK:
}
CASE(OP_ERR, B) {
- mrb_value msg = mrb_str_dup(mrb, pool[a]);
+ size_t len = pool[a].tt >> 2;
mrb_value exc;
- exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
- ERR_PC_SET(mrb, pc);
+ mrb_assert((pool[a].tt&IREP_TT_NFLAG)==0);
+ exc = mrb_exc_new(mrb, E_LOCALJUMP_ERROR, pool[a].u.str, len);
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
+ CASE(OP_SENDVK, BB) { /* not yet implemented */
+ NEXT;
+ }
+
CASE(OP_EXT1, Z) {
insn = READ_B();
switch (insn) {
-#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); goto L_OP_ ## insn ## _BODY;
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY;
#include "mruby/ops.h"
#undef OPCODE
}
@@ -2932,7 +2898,7 @@ RETRY_TRY_BLOCK:
CASE(OP_EXT2, Z) {
insn = READ_B();
switch (insn) {
-#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); goto L_OP_ ## insn ## _BODY;
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY;
#include "mruby/ops.h"
#undef OPCODE
}
@@ -2942,7 +2908,7 @@ RETRY_TRY_BLOCK:
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;
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY;
#include "mruby/ops.h"
#undef OPCODE
}
@@ -2952,14 +2918,17 @@ RETRY_TRY_BLOCK:
CASE(OP_STOP, Z) {
/* stop VM */
- L_STOP:
- while (mrb->c->eidx > 0) {
- ecall(mrb);
+ CHECKPOINT_RESTORE(RBREAK_TAG_STOP) {
+ /* do nothing */
}
- mrb->c->cibase->ridx = 0;
- ERR_PC_CLR(mrb);
+ CHECKPOINT_MAIN(RBREAK_TAG_STOP) {
+ UNWIND_ENSURE(mrb, mrb->c->ci, pc, RBREAK_TAG_STOP, proc, mrb_nil_value());
+ }
+ CHECKPOINT_END(RBREAK_TAG_STOP);
+ L_STOP:
mrb->jmp = prev_jmp;
if (mrb->exc) {
+ mrb_assert(mrb->exc->tt == MRB_TT_EXCEPTION);
return mrb_obj_value(mrb->exc);
}
return regs[irep->nlocals];
@@ -2969,14 +2938,19 @@ RETRY_TRY_BLOCK:
#undef regs
}
MRB_CATCH(&c_jmp) {
+ mrb_callinfo *ci = mrb->c->ci;
+ while (ci > mrb->c->cibase && ci->acc == CI_ACC_DIRECT) {
+ ci = cipop(mrb);
+ }
exc_catched = TRUE;
+ pc = ci->pc;
goto RETRY_TRY_BLOCK;
}
MRB_END_EXC(&c_jmp);
}
-MRB_API mrb_value
-mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
+static mrb_value
+mrb_run(mrb_state *mrb, const struct RProc *proc, mrb_value self)
{
if (mrb->c->ci->argc < 0) {
return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */
@@ -2987,33 +2961,29 @@ mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
}
MRB_API mrb_value
-mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
+mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_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) {
+ mrb_vm_ci_env_set(mrb->c->ci, NULL);
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;
+ cipush(mrb, 0, CI_ACC_SKIP, mrb->object_class, NULL, 0, 0);
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)
+#if defined(MRB_USE_CXX_EXCEPTION) && defined(__cplusplus)
+# if !defined(MRB_USE_CXX_ABI)
} /* end of extern "C" */
# endif
mrb_int mrb_jmpbuf::jmpbuf_id = 0;
-# if !defined(MRB_ENABLE_CXX_ABI)
+# if !defined(MRB_USE_CXX_ABI)
extern "C" {
# endif
#endif
diff --git a/bin/.gitkeep b/super-linter.report/.keep
index e69de29bb..e69de29bb 100644
--- a/bin/.gitkeep
+++ b/super-linter.report/.keep
diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake
index 84e69ebee..12e0d4602 100644
--- a/tasks/benchmark.rake
+++ b/tasks/benchmark.rake
@@ -5,14 +5,14 @@ end
$dat_files = []
def bm_files
- Dir.glob("#{MRUBY_ROOT}/benchmark/bm_*.rb")
+ Dir.glob("#{MRUBY_ROOT}/benchmark/bm_*.rb").sort
end
def build_config_name
- if ENV['MRUBY_CONFIG']
+ if !ENV['MRUBY_CONFIG'].to_s.empty?
File.basename(ENV['MRUBY_CONFIG'], '.rb').gsub('build_config_', '')
else
- "build"
+ "bm"
end
end
@@ -50,7 +50,7 @@ end
MRuby.each_target do |target|
- next if target.name == 'host'
+ next if target.name == 'host' || target.internal?
mruby_bin = "#{target.build_dir}/bin/mruby"
bm_files.each do |bm_file|
@@ -67,8 +67,8 @@ MRuby.each_target do |target|
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)
+ str = %x{(time -p #{mruby_bin} #{bm_file}) 2>&1 >/dev/null}
+ str.scan(/\d+\.\d+$/).map(&:to_f) # [real, user, sys]
end
File.open(task.name, "w") do |f|
diff --git a/tasks/bin.rake b/tasks/bin.rake
new file mode 100644
index 000000000..5c764458d
--- /dev/null
+++ b/tasks/bin.rake
@@ -0,0 +1,23 @@
+MRuby.each_target do |build|
+ if build.host? && build.mrbc_build && !build.gems["mruby-bin-mrbc"]
+ exe = build.exefile("#{build.mrbc_build.build_dir}/bin/mrbc")
+ build.products << build.define_installer(exe)
+ end
+
+ build.bins.each{|bin| build.products << define_installer_if_needed(bin)}
+
+ build.gems.each do |gem|
+ linker_attrs = build.gems.linker_attrs(gem)
+ gem.bins.each do |bin|
+ exe = build.exefile("#{build.build_dir}/bin/#{bin}")
+ objs = Dir["#{gem.dir}/tools/#{bin}/*.{c,cpp,cxx,cc}"].map do |f|
+ build.objfile(f.pathmap("#{gem.build_dir}/tools/#{bin}/%n"))
+ end
+ file exe => objs.concat(build.libraries) do |t|
+ build.linker.run t.name, t.prerequisites, *linker_attrs
+ end
+
+ build.products << define_installer_if_needed(bin)
+ end
+ end
+end
diff --git a/tasks/core.rake b/tasks/core.rake
new file mode 100644
index 000000000..a47722e8b
--- /dev/null
+++ b/tasks/core.rake
@@ -0,0 +1,12 @@
+as_cxx_srcs = %w[vm error gc].map{|name| "#{MRUBY_ROOT}/src/#{name}.c"}
+
+MRuby.each_target do
+ objs = Dir.glob("#{MRUBY_ROOT}/src/*.c").map do |src|
+ if cxx_exception_enabled? && as_cxx_srcs.include?(src)
+ compile_as_cxx(src)
+ else
+ objfile(src.pathmap("#{build_dir}/src/%n"))
+ end
+ end
+ self.libmruby_core_objs << objs
+end
diff --git a/tasks/doc.rake b/tasks/doc.rake
new file mode 100644
index 000000000..8013ed038
--- /dev/null
+++ b/tasks/doc.rake
@@ -0,0 +1,60 @@
+desc 'generate document'
+task :doc => %w[doc:api doc:capi]
+
+namespace :doc do
+ desc 'generate yard docs'
+ task :api do
+ begin
+ sh "mrbdoc"
+ rescue
+ puts "ERROR: To generate yard documentation, you should install yard-mruby gem."
+ puts " $ gem install yard-mruby yard-coderay"
+ end
+ end
+
+ desc 'generate doxygen docs'
+ task :capi do
+ begin
+ sh "doxygen Doxyfile"
+ rescue
+ puts "ERROR: To generate C API documents, you need Doxygen."
+ puts " $ sudo apt-get install doxygen"
+ end
+ end
+
+ desc 'clean all built docs'
+ task :clean => %w[clean:api clean:capi]
+
+ namespace :clean do
+ desc 'clean yard docs'
+ task :api do
+ rm_rf 'doc/api'
+ end
+
+ desc 'clean doxygen docs'
+ task :capi do
+ rm_rf 'doc/capi'
+ end
+ end
+
+ namespace :view do
+ desc 'open yard docs'
+ task :api do
+ sh 'xdg-open doc/api/index.html'
+ end
+
+ desc 'open doxygen docs'
+ task :capi do
+ sh 'xdg-open doc/capi/html/index.html'
+ end
+ end
+end
+
+# deprecated
+task "api_doc" => "doc:api"
+task "capi_doc" => "doc:capi"
+task "clean_doc" => "doc:clean"
+task "clean_api_doc" => "doc:clean:api"
+task "clean_capi_doc" => "doc:clean:capi"
+task "view_api" => "doc:view:api"
+task "view_capi" => "doc:view:capi"
diff --git a/tasks/gitlab.rake b/tasks/gitlab.rake
deleted file mode 100644
index 471172377..000000000
--- a/tasks/gitlab.rake
+++ /dev/null
@@ -1,118 +0,0 @@
-CI_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
diff --git a/tasks/libmruby.rake b/tasks/libmruby.rake
index b6ef29986..9a7a57ff7 100644
--- a/tasks/libmruby.rake
+++ b/tasks/libmruby.rake
@@ -1,25 +1,38 @@
MRuby.each_target do
- file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t|
+ file libmruby_core_static => libmruby_core_objs.flatten do |t|
archiver.run t.name, t.prerequisites
end
- file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libfile("#{build_dir}/lib/libmruby")] do |t|
- FileUtils.mkdir_p File.dirname t.name
+ products << libmruby_core_static
+
+ next unless libmruby_enabled?
+
+ 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|
+ 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_CC = #{cc.command}"
+ f.puts "MRUBY_LD = #{linker.command}"
+
+ libgems = gems.reject{|g| g.bin?}
+ gem_flags = libgems.map {|g| g.linker.flags }
+ gem_library_paths = libgems.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 }
+ gem_flags_before_libraries = libgems.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 }
+ gem_libraries = libgems.map {|g| g.linker.libraries }
f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries)}"
- f.puts "MRUBY_LIBMRUBY_PATH = #{libfile("#{build_dir}/lib/libmruby")}"
+ f.puts "MRUBY_LIBMRUBY_PATH = #{libmruby_static}"
end
end
- task :all => "#{build_dir}/lib/libmruby.flags.mak"
+
+ products << libmruby_static
end
diff --git a/tasks/mrbgems.rake b/tasks/mrbgems.rake
index e2aeb1514..4f24cfd93 100644
--- a/tasks/mrbgems.rake
+++ b/tasks/mrbgems.rake
@@ -1,14 +1,16 @@
MRuby.each_target do
+ active_gems_txt = "#{build_dir}/mrbgems/active_gems.txt"
+
if enable_gems?
# set up all gems
gems.each(&:setup)
gems.check self
# loader all gems
- self.libmruby << objfile("#{build_dir}/mrbgems/gem_init")
+ 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"
+ file "#{build_dir}/mrbgems/gem_init.c" => [active_gems_txt, MRUBY_CONFIG, __FILE__] do |t|
+ 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|
@@ -35,7 +37,7 @@ MRuby.each_target do
f.puts %Q[]
f.write gem_func_decls
unless gem_final_calls.empty?
- f.puts %Q[]
+ f.puts %Q[]
f.puts %Q[static void]
f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {]
f.write gem_final_calls
@@ -51,11 +53,21 @@ MRuby.each_target do
end
end
+ file active_gems_txt => :generate_active_gems_txt
+ task :generate_active_gems_txt do |t|
+ def t.timestamp; Time.at(0) end
+ active_gems = gems.sort_by(&:name).inject(""){|s, g| s << "#{g.name}\n"}
+ if !File.exist?(active_gems_txt) || File.read(active_gems_txt) != active_gems
+ mkdir_p File.dirname(active_gems_txt)
+ File.write(active_gems_txt, active_gems)
+ end
+ end
+
# legal documents
file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t|
- FileUtils.mkdir_p File.dirname t.name
+ mkdir_p File.dirname t.name
open(t.name, 'w+') do |f|
- f.puts <<LEGAL
+ f.puts <<LEGAL
Copyright (c) #{Time.now.year} mruby developers
Permission is hereby granted, free of charge, to any person obtaining a
@@ -82,7 +94,7 @@ LEGAL
Additional Licenses
-Due to the reason that you choosed additional mruby packages (GEMS),
+Due to the reason that you chose additional mruby packages (GEMS),
please check the following additional licenses too:
GEMS_LEGAL
diff --git a/tasks/mrblib.rake b/tasks/mrblib.rake
new file mode 100644
index 000000000..485375e55
--- /dev/null
+++ b/tasks/mrblib.rake
@@ -0,0 +1,40 @@
+MRuby.each_target do
+ next unless libmruby_enabled?
+
+ src = "#{build_dir}/mrblib/mrblib.c"
+ rbfiles = Dir["#{MRUBY_ROOT}/mrblib/*.rb"].sort!
+
+ self.libmruby_objs << objfile(src.ext)
+
+ file src => [mrbcfile, __FILE__, *rbfiles] do |t|
+ if presym_enabled?
+ cdump = true
+ suffix = "proc"
+ else
+ cdump = false
+ suffix = "irep"
+ end
+ mkdir_p File.dirname(t.name)
+ File.open(t.name, 'w') do |f|
+ _pp "GEN", "mrblib/*.rb", "#{t.name.relative_path}"
+ f.puts %Q[/*]
+ f.puts %Q[ * This file is loading the mrblib]
+ 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[ */]
+ unless presym_enabled?
+ f.puts %Q[#include <mruby.h>]
+ f.puts %Q[#include <mruby/irep.h>]
+ end
+ mrbc.run f, rbfiles, "mrblib_#{suffix}", cdump: cdump, static: true
+ f.puts %Q[void]
+ f.puts %Q[mrb_init_mrblib(mrb_state *mrb)]
+ f.puts %Q[{]
+ f.puts %Q[ mrblib_#{suffix}_init_syms(mrb);] if cdump
+ f.puts %Q[ mrb_load_#{suffix}(mrb, mrblib_#{suffix});]
+ f.puts %Q[}]
+ end
+ end
+end
diff --git a/tasks/presym.rake b/tasks/presym.rake
new file mode 100644
index 000000000..1ebc1a737
--- /dev/null
+++ b/tasks/presym.rake
@@ -0,0 +1,48 @@
+all_prerequisites = ->(task_name, prereqs) do
+ Rake::Task[task_name].prerequisites.each do |prereq_name|
+ next if prereqs[prereq_name]
+ prereqs[prereq_name] = true
+ all_prerequisites.(Rake::Task[prereq_name].name, prereqs)
+ end
+end
+
+MRuby.each_target do |build|
+ gensym_task = task(:gensym)
+ next unless build.presym_enabled?
+
+ presym = build.presym
+
+ include_dir = "#{build.build_dir}/include"
+ build.compilers.each{|c| c.include_paths << include_dir}
+ build.gems.each{|gem| gem.compilers.each{|c| c.include_paths << include_dir}}
+
+ prereqs = {}
+ ppps = []
+ build_dir = "#{build.build_dir}/"
+ mrbc_build_dir = "#{build.mrbc_build.build_dir}/" if build.mrbc_build
+ build.products.each{|product| all_prerequisites.(product, prereqs)}
+ prereqs.each_key do |prereq|
+ next unless File.extname(prereq) == build.exts.object
+ next unless prereq.start_with?(build_dir)
+ next if mrbc_build_dir && prereq.start_with?(mrbc_build_dir)
+ ppp = prereq.ext(build.exts.presym_preprocessed)
+ if Rake.application.lookup(ppp) ||
+ Rake.application.enhance_with_matching_rule(ppp)
+ ppps << ppp
+ end
+ end
+
+ file presym.list_path => ppps do
+ presyms = presym.scan(ppps)
+ current_presyms = presym.read_list if File.exist?(presym.list_path)
+ update = presyms != current_presyms
+ presym.write_list(presyms) if update
+ mkdir_p presym.header_dir
+ %w[id table].each do |type|
+ next if !update && File.exist?(presym.send("#{type}_header_path"))
+ presym.send("write_#{type}_header", presyms)
+ end
+ end
+
+ gensym_task.enhance([presym.list_path])
+end
diff --git a/tasks/test.rake b/tasks/test.rake
new file mode 100644
index 000000000..32a03fce6
--- /dev/null
+++ b/tasks/test.rake
@@ -0,0 +1,69 @@
+desc "build and run all mruby tests"
+task :test => "test:build" do
+ Rake::Task["test:run"].invoke
+end
+
+namespace :test do |test_ns|
+ desc "build and run library tests"
+ task :lib => "build:lib" do
+ test_ns["run:lib"].invoke
+ end
+
+ desc "build and run command binaries tests"
+ task :bin => "rake:all" do
+ test_ns["run:bin"].invoke
+ end
+
+ desc "build all mruby tests"
+ task :build => "build:lib"
+
+ namespace :build do |test_build_ns|
+ desc "build library tests"
+ task :lib => "rake:all" do
+ MRuby.each_target{|build| build.gem(core: 'mruby-test')}
+ test = test_build_ns["lib_without_loading_gem"]
+ test.invoke if test
+ end
+ end
+
+ desc "run all mruby tests"
+ task :run
+
+ namespace :run do
+ desc "run library tests"
+ task :lib
+
+ desc "run command binaries tests"
+ task :bin
+ end
+end
+
+MRuby.each_target do |build|
+ if build.test_enabled?
+ t = task "test:build:lib_without_loading_gem:#{build.name}" do
+ gem = build.gems["mruby-test"]
+ gem.setup
+ gem.setup_compilers
+ Rake::Task[build.define_installer_if_needed("mrbtest")].invoke
+ end
+ task "test:build:lib_without_loading_gem" => t
+
+ t = task "test:run:lib:#{build.name}" do
+ build.run_test
+ end
+ task "test:run" => t
+ task "test:run:lib" => t
+ end
+ if build.bintest_enabled?
+ t = task "test:run:bin:#{build.name}" do
+ build.run_bintest
+ end
+ task "test:run" => t
+ task "test:run:bin" => t
+ end
+end
+
+task :clean do
+ host = MRuby.targets["host"]
+ rm_f host.exefile("#{host.class.install_dir}/mrbtest") if host && host.test_enabled?
+end
diff --git a/tasks/toolchains/android.rake b/tasks/toolchains/android.rake
index c7df9ef80..48526bc43 100644
--- a/tasks/toolchains/android.rake
+++ b/tasks/toolchains/android.rake
@@ -10,6 +10,7 @@ class MRuby::Toolchain::Android
~/Android/Sdk/ndk-bundle
%LOCALAPPDATA%/Android/android-sdk/ndk-bundle
%LOCALAPPDATA%/Android/android-ndk
+ %LOCALAPPDATA%/Android/Sdk/ndk/*
~/Library/Android/sdk/ndk-bundle
~/Library/Android/ndk
}
@@ -40,6 +41,19 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
end
end
+ class SysrootNotReady < StandardError
+ def message
+ <<-EOM
+Couldn't find standard header files
+Please Move/Copy important file inside
+ <NDK_HOME>/sysroot/usr/include/
+to
+ <NDK_HOME>/platforms/<ANDROID_VERSION>/<ARCH>/usr/include/
+Higher NDK version will be use.
+ EOM
+ end
+ end
+
attr_reader :params
def initialize(params)
@@ -67,13 +81,32 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
end
def home_path
- @home_path ||= Pathname(
+ @home_path ||= Pathname.new(
params[:ndk_home] ||
ENV['ANDROID_NDK_HOME'] ||
DEFAULT_NDK_HOMES.find { |path|
path.gsub! '%LOCALAPPDATA%', ENV['LOCALAPPDATA'] || '%LOCALAPPDATA%'
path.gsub! '\\', '/'
path.gsub! '~', Dir.home || '~'
+ path.gsub!('*') do
+ next nil unless path[-1] == "*"
+ dirs = Dir.glob(path).collect do |d|
+ m = d.match(/(\d+)\.(\d+)\.(\d+)$/)
+ m ? [m[1], m[2], m[3]].collect { |v| v.to_i } : nil
+ end
+ dirs.compact!
+ dirs.sort! do |before, after|
+ f = 0
+ if (f = (after.first <=> before.first)) != 0
+ next f
+ elsif (f = (after[1] <=> before[1])) != 0
+ next f
+ else
+ next after.last <=> before.last
+ end
+ end
+ dirs.empty? ? nil.to_s : dirs.first.join(".")
+ end
File.directory?(path)
} || raise(AndroidNDKHomeNotFound)
)
@@ -124,7 +157,7 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
path = home_path.join('toolchains', 'llvm' , 'prebuilt', 'windows*')
Dir.glob(path.to_s){ |item|
next if File.file?(item)
- path = Pathname(item)
+ path = Pathname.new(item)
break
}
path.basename
@@ -146,7 +179,8 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
end
def sysroot
- @sysroot ||= home_path.join('platforms', platform,
+ return @sysroot if @sysroot
+ sysroot_path = home_path.join('platforms', platform,
case arch
when /armeabi/ then 'arch-arm'
when /arm64-v8a/ then 'arch-arm64'
@@ -156,6 +190,11 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
when /mips/ then 'arch-mips'
end
).to_s
+ if Dir.exist?(File.join(sysroot_path, "usr", "include"))
+ return @sysroot = sysroot_path
+ else
+ raise(SysrootNotReady)
+ end
end
def platform
@@ -259,6 +298,12 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter
def cflags
flags = []
+ case RUBY_PLATFORM
+ when /mswin|mingw|win32/
+ # Build for Android don't need window flag
+ flags += %W(-U_WIN32 -U_WIN64)
+ end
+
flags += %W(-MMD -MP -D__android__ -DANDROID --sysroot="#{sysroot}")
flags += ctarget
case toolchain
diff --git a/tasks/toolchains/clang.rake b/tasks/toolchains/clang.rake
index c75fa030c..543cb73db 100644
--- a/tasks/toolchains/clang.rake
+++ b/tasks/toolchains/clang.rake
@@ -1,9 +1,8 @@
MRuby::Toolchain.new(:clang) do |conf, _params|
- toolchain :gcc
+ toolchain :gcc, default_command: 'clang'
[conf.cc, conf.objc, conf.asm].each do |cc|
- cc.command = ENV['CC'] || 'clang'
+ cc.flags << '-Wzero-length-array' unless ENV['CFLAGS']
end
- conf.cxx.command = ENV['CXX'] || 'clang++'
- conf.linker.command = ENV['LD'] || 'clang'
+ conf.cxx.flags << '-Wzero-length-array' unless ENV['CXXFLAGS'] || ENV['CFLAGS']
end
diff --git a/tasks/toolchains/gcc.rake b/tasks/toolchains/gcc.rake
index e0eb36f26..51bda6517 100644
--- a/tasks/toolchains/gcc.rake
+++ b/tasks/toolchains/gcc.rake
@@ -1,34 +1,35 @@
-MRuby::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
+MRuby::Toolchain.new(:gcc) do |conf, params|
+ default_command = params[:default_command] || 'gcc'
+ compiler_flags = %w(-g -O3 -Wall -Wundef)
+ c_mandatory_flags = %w(-std=gnu99)
+ cxx_invalid_flags = %w(-Werror-implicit-function-declaration)
+ compile_opt = '%{flags} -o "%{outfile}" "%{infile}"'
- [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'
+ [conf.cc, conf.objc, conf.asm, conf.cxx].each do |compiler|
+ if compiler == conf.cxx
+ compiler.command = ENV['CXX'] || conf.cc.command.sub(/g\Kcc|$/, '++')
+ compiler.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || compiler_flags]
+ else
+ compiler.command = ENV['CC'] || default_command
+ compiler.flags = [c_mandatory_flags, ENV['CFLAGS'] || [compiler_flags, cxx_invalid_flags, %w(-Wwrite-strings)]]
+ end
+ compiler.option_include_path = %q[-I"%s"]
+ compiler.option_define = '-D%s'
+ compiler.compile_options = "-MMD -c #{compile_opt}"
+ compiler.preprocess_options = "-E -P #{compile_opt}"
+ compiler.cxx_compile_flag = '-x c++ -std=gnu++03'
+ compiler.cxx_exception_flag = '-fexceptions'
+ compiler.cxx_invalid_flags = c_mandatory_flags + cxx_invalid_flags
end
conf.linker do |linker|
- linker.command = ENV['LD'] || 'gcc'
+ linker.command = ENV['LD'] || ENV['CXX'] || ENV['CC'] || default_command
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}'
+ 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|
@@ -36,8 +37,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
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
+ return include_paths if $?.exitstatus != 0
@frameworks = []
@header_search_paths = result.lines.map { |v|
diff --git a/tasks/toolchains/openwrt.rake b/tasks/toolchains/openwrt.rake
index 1637f6d91..6ef3f4e4a 100644
--- a/tasks/toolchains/openwrt.rake
+++ b/tasks/toolchains/openwrt.rake
@@ -1,26 +1,20 @@
# 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'
+ [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc|
+ if cc == conf.cxx
+ cc.command = ENV['TARGET_CXX']
+ cc.flags = ENV['TARGET_CXXFLAGS']
+ else
+ cc.command = ENV['TARGET_CC']
+ cc.flags = ENV['TARGET_CFLAGS']
+ end
+ cc.option_include_path = %q[-I"%s"]
cc.option_define = '-D%s'
- cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
+ cc.compile_options = '%{flags} -MMD -o "%{outfile}" -c "%{infile}"'
+ cc.preprocess_options = '%{flags} -o "%{outfile}" -E -P "%{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']
@@ -28,11 +22,11 @@ MRuby::Toolchain.new(:openwrt) do |conf|
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}'
+ 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}'
+ archiver.archive_options = 'rs "%{outfile}" %{objs}'
end
end
diff --git a/tasks/toolchains/visualcpp.rake b/tasks/toolchains/visualcpp.rake
index b008273a2..00e364082 100644
--- a/tasks/toolchains/visualcpp.rake
+++ b/tasks/toolchains/visualcpp.rake
@@ -1,25 +1,21 @@
MRuby::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'
+ compiler_flags = %w(/nologo /W3 /MD /O2 /D_CRT_SECURE_NO_WARNINGS)
+ [conf.cc, conf.cxx].each do |compiler|
+ if compiler == conf.cc
+ compiler.command = ENV['CC'] || 'cl.exe'
+ # C4013: implicit function declaration
+ compiler.flags = [*(ENV['CFLAGS'] || compiler_flags + %w(/we4013))]
+ else
+ compiler.command = ENV['CXX'] || 'cl.exe'
+ compiler.flags = [*(ENV['CXXFLAGS'] || ENV['CFLAGS'] || compiler_flags + %w(/EHs))]
+ end
+ compiler.defines = %w(MRB_STACK_EXTEND_DOUBLING)
+ compiler.option_include_path = %q[/I"%s"]
+ compiler.option_define = '/D%s'
+ compiler.compile_options = %Q[/Zi /c /Fo"%{outfile}" %{flags} "%{infile}"]
+ compiler.preprocess_options = %Q[/EP %{flags} "%{infile}" > "%{outfile}"]
+ compiler.cxx_compile_flag = '/TP'
+ compiler.cxx_exception_flag = '/EHs'
end
conf.linker do |linker|
@@ -29,22 +25,22 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
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}"
+ linker.link_options = %Q[%{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}'
+ archiver.archive_options = '/nologo /OUT:"%{outfile}" %{objs}'
end
conf.yacc do |yacc|
yacc.command = ENV['YACC'] || 'bison.exe'
- yacc.compile_options = '-o %{outfile} %{infile}'
+ yacc.compile_options = %q[-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}'
+ gperf.compile_options = %q[-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|
@@ -54,16 +50,4 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
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
diff --git a/test/assert.rb b/test/assert.rb
index a9baae5e1..8cd2b375b 100644
--- a/test/assert.rb
+++ b/test/assert.rb
@@ -1,17 +1,52 @@
+$undefined = Object.new
$ok_test = 0
$ko_test = 0
$kill_test = 0
+$warning_test = 0
+$skip_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
+# For bintest on Ruby
+unless RUBY_ENGINE == "mruby"
+ def t_print(*args)
+ print(*args)
+ $stdout.flush
+ nil
+ end
+
+ def _str_match?(pattern, str)
+ File.fnmatch?(pattern, str, File::FNM_EXTGLOB|File::FNM_DOTMATCH)
+ end
+end
+
+class Array
+ def _assertion_join
+ join("-")
+ end
+end
+
+class String
+ def _assertion_indent(indent)
+ indent = indent.to_s
+ off = 0
+ str = self
+ while nl = index("\n", off)
+ nl += 1
+ nl += 1 while slice(nl) == "\n"
+ break if nl >= size
+ str = indent.dup if off == 0
+ str += slice(off, nl - off) + indent
+ off = nl
+ end
+
+ if off == 0
+ str = indent + self
+ else
+ str += slice(off..-1)
+ end
+
+ str
end
end
@@ -19,16 +54,19 @@ 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
+ msg += " [#{iso}]" if iso && !iso.empty?
+ msg += " => #{e}" if e && !e.to_s.empty?
+ if Object.const_defined?(:GEMNAME)
+ msg += " (#{GEMNAME == 'mruby-test' ? 'core' : "mrbgems: #{GEMNAME}"})"
+ end
+ if $mrbtest_assert
$mrbtest_assert.each do |idx, assert_msg, diff|
- msg += "\n - Assertion[#{idx}] Failed: #{assert_msg}\n#{diff}"
+ msg += "\n - Assertion[#{idx}]"
+ msg += " #{assert_msg}." if assert_msg && !assert_msg.empty?
+ msg += "\n#{diff}" if diff && !diff.empty?
end
end
- msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt
+ msg += "\nbacktrace:\n #{bt.join("\n ")}" if bt && !bt.empty?
msg
end
@@ -40,209 +78,326 @@ end
# iso : The ISO reference code of the feature
# which will be tested by this
# assertion
-def assert(str = 'Assertion failed', iso = '')
+def assert(str = 'assert', iso = '')
t_print(str, (iso != '' ? " [#{iso}]" : ''), ' : ') if $mrbtest_verbose
begin
+ $mrbtest_child_noassert ||= [0]
+ $mrbtest_child_noassert << 0
+ parent_asserts = $asserts
+ $asserts = []
+ parent_mrbtest_assert = $mrbtest_assert
$mrbtest_assert = []
- $mrbtest_assert_idx = 0
+
+ if $mrbtest_assert_idx && !$mrbtest_assert_idx.empty?
+ $mrbtest_assert_idx[-1] += 1
+ $mrbtest_assert_idx << 0
+ else
+ $mrbtest_assert_idx = [0]
+ class << $mrbtest_assert_idx
+ alias to_s _assertion_join
+ end
+ end
+
yield
- if($mrbtest_assert.size > 0)
- $asserts.push(assertion_string('Fail: ', str, iso, nil))
- $ko_test += 1
- t_print('F')
+ if $mrbtest_assert.size > 0
+ if $mrbtest_assert.size == $mrbtest_child_noassert[-1]
+ $asserts.push(assertion_string('Skip: ', str, iso))
+ $mrbtest_child_noassert[-2] += 1
+ $skip_test += 1
+ t_print('?')
+ else
+ $asserts.push(assertion_string('Fail: ', str, iso))
+ $ko_test += 1
+ t_print('F')
+ end
+ elsif $mrbtest_assert_idx[-1] == 0
+ $asserts.push(assertion_string('Warn: ', str, iso, 'no assertion'))
+ $warning_test += 1
+ t_print('W')
else
$ok_test += 1
t_print('.')
end
+ rescue MRubyTestSkip => e
+ $asserts.push(assertion_string('Skip: ', str, iso, e))
+ $skip_test += 1
+ $mrbtest_child_noassert[-2] += 1
+ t_print('?')
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('?')
+ $asserts.push(assertion_string("#{e.class}: ", str, iso, e, e.backtrace))
+ $kill_test += 1
+ t_print('X')
+ ensure
+ if $mrbtest_assert_idx.size > 1
+ $asserts.each do |mesg|
+ idx = $mrbtest_assert_idx[0..-2]._assertion_join
+ mesg = mesg._assertion_indent(" ")
+
+ # Give `mesg` as a `diff` argument to avoid adding extra periods.
+ parent_mrbtest_assert << [idx, nil, mesg]
+ end
else
- $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
- $kill_test += 1
- t_print('X')
+ parent_asserts.concat $asserts
end
- ensure
- $mrbtest_assert = nil
+ $asserts = parent_asserts
+
+ $mrbtest_assert = parent_mrbtest_assert
+ $mrbtest_assert_idx.pop
+ $mrbtest_assert_idx = nil if $mrbtest_assert_idx.empty?
+ $mrbtest_child_noassert.pop
+
+ nil
end
t_print("\n") if $mrbtest_verbose
end
def assertion_diff(exp, act)
- " Expected: #{exp.inspect}\n" +
+ " 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])
+def assert_true(obj, msg = nil, diff = nil)
+ if $mrbtest_assert_idx && $mrbtest_assert_idx.size > 0
+ $mrbtest_assert_idx[-1] += 1
+ unless obj == true
+ diff ||= " Expected #{obj.inspect} to be true."
+ $mrbtest_assert.push([$mrbtest_assert_idx.to_s, msg, diff])
end
end
- ret
+ obj
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
+def assert_false(obj, msg = nil, diff = nil)
+ unless obj == false
+ diff ||= " Expected #{obj.inspect} to be false."
+ end
+ assert_true(!obj, msg, diff)
+end
- $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
+def assert_equal(exp, act_or_msg = nil, msg = nil, &block)
+ ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block)
+ unless ret
+ diff = assertion_diff(exp, act)
+ end
+ assert_true(ret, msg, diff)
+end
+
+def assert_not_equal(exp, act_or_msg = nil, msg = nil, &block)
+ ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block)
+ if ret
+ diff = " Expected #{act.inspect} to not be equal to #{exp.inspect}."
+ end
+ assert_true(!ret, msg, diff)
+end
+
+def assert_same(*args); _assert_same(true, *args) end
+def assert_not_same(*args); _assert_same(false, *args) end
+def _assert_same(affirmed, exp, act, msg = nil)
+ unless ret = exp.equal?(act) == affirmed
+ exp_str, act_str = [exp, act].map do |o|
+ "#{o.inspect} (class=#{o.class}, oid=#{o.__id__})"
end
+ diff = " Expected #{act_str} to #{'not ' unless affirmed}be the same as #{exp_str}."
end
- !ret
+ assert_true(ret, msg, diff)
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
+def assert_nil(obj, msg = nil)
+ unless ret = obj.nil?
+ diff = " Expected #{obj.inspect} to be nil."
end
+ assert_true(ret, msg, diff)
+end
- msg = "Expected to be equal" unless msg
- diff = assertion_diff(exp, act)
- assert_true(exp == act, msg, diff)
+def assert_not_nil(obj, msg = nil)
+ if ret = obj.nil?
+ diff = " Expected #{obj.inspect} to not be nil."
+ end
+ assert_false(ret, 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
+def assert_include(*args); _assert_include(true, *args) end
+def assert_not_include(*args); _assert_include(false, *args) end
+def _assert_include(affirmed, collection, obj, msg = nil)
+ unless ret = collection.include?(obj) == affirmed
+ diff = " Expected #{collection.inspect} to #{'not ' unless affirmed}include #{obj.inspect}."
end
+ assert_true(ret, msg, diff)
+end
- msg = "Expected to be not equal" unless msg
- diff = assertion_diff(exp, act)
- assert_false(exp == act, msg, diff)
+def assert_predicate(*args); _assert_predicate(true, *args) end
+def assert_not_predicate(*args); _assert_predicate(false, *args) end
+def _assert_predicate(affirmed, obj, op, msg = nil)
+ unless ret = obj.__send__(op) == affirmed
+ diff = " Expected #{obj.inspect} to #{'not ' unless affirmed}be #{op}."
+ end
+ assert_true(ret, 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)
+def assert_operator(*args); _assert_operator(true, *args) end
+def assert_not_operator(*args); _assert_operator(false, *args) end
+def _assert_operator(affirmed, obj1, op, obj2 = $undefined, msg = nil)
+ return _assert_predicate(affirmed, obj1, op, msg) if $undefined.equal?(obj2)
+ unless ret = obj1.__send__(op, obj2) == affirmed
+ diff = " Expected #{obj1.inspect} to #{'not ' unless affirmed}be #{op} #{obj2.inspect}."
+ end
+ assert_true(ret, 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)
+##
+# Fail unless +str+ matches against +pattern+.
+#
+# +pattern+ is interpreted as pattern for File.fnmatch?. It may contain the
+# following metacharacters:
+#
+# <code>*</code> ::
+# Matches any string.
+#
+# <code>?</code> ::
+# Matches any one character.
+#
+# <code>[_SET_]</code>, <code>[^_SET_]</code> (<code>[!_SET_]</code>) ::
+# Matches any one character in _SET_. Behaves like character sets in
+# Regexp, including set negation (<code>[^a-z]</code>).
+#
+# <code>{_A_,_B_}</code> ::
+# Matches pattern _A_ or pattern _B_.
+#
+# <code> \ </code> ::
+# Escapes the next character.
+def assert_match(*args); _assert_match(true, *args) end
+def assert_not_match(*args); _assert_match(false, *args) end
+def _assert_match(affirmed, pattern, str, msg = nil)
+ unless ret = _str_match?(pattern, str) == affirmed
+ diff = " Expected #{pattern.inspect} to #{'not ' unless affirmed}match #{str.inspect}."
+ end
+ assert_true(ret, 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)
+##
+# Fails unless +obj+ is a kind of +cls+.
+def assert_kind_of(cls, obj, msg = nil)
+ unless ret = obj.kind_of?(cls)
+ diff = " Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}."
+ end
+ assert_true(ret, msg, diff)
end
-def assert_raise(*exc)
- return true unless $mrbtest_assert
- $mrbtest_assert_idx += 1
+##
+# Fails unless +exp+ is equal to +act+ in terms of a Float
+def assert_float(exp, act, msg = nil)
+ e, a = exp.to_f, act.to_f
+ if e.finite? && a.finite? && (n = (e - a).abs) > Mrbtest::FLOAT_TOLERANCE
+ flunk(msg, " Expected |#{exp} - #{act}| (#{n}) to be <= #{Mrbtest::FLOAT_TOLERANCE}.")
+ elsif (e.infinite? || a.infinite?) && e != a ||
+ e.nan? && !a.nan? || !e.nan? && a.nan?
+ flunk(msg, " Expected #{act} to be #{exp}.")
+ else
+ pass
+ end
+end
+def assert_raise(*exc)
msg = (exc.last.is_a? String) ? exc.pop : nil
-
+ exc = exc.empty? ? StandardError : exc.size == 1 ? exc[0] : exc
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 *exc => e
+ pass
+ e
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
+ diff = " #{exc} exception expected, not\n" \
+ " Class: <#{e.class}>\n" \
+ " Message: <#{e}>"
+ flunk(msg, diff)
+ else
+ diff = " #{exc} expected but nothing was raised."
+ flunk(msg, diff)
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
+ diff = " Exception raised:\n" \
+ " Class: <#{e.class}>\n" \
+ " Message: <#{e}>"
+ flunk(msg, diff)
+ else
+ pass
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)
+def assert_raise_with_message(*args, &block)
+ _assert_raise_with_message(:plain, *args, &block)
end
+def assert_raise_with_message_pattern(*args, &block)
+ _assert_raise_with_message(:pattern, *args, &block)
+end
+def _assert_raise_with_message(type, exc, exp_msg, msg = nil, &block)
+ e = msg ? assert_raise(exc, msg, &block) : assert_raise(exc, &block)
+ e ? ($mrbtest_assert_idx[-1]-=1) : (return e)
-##
-# 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
+ err_msg = e.message
+ unless ret = type == :pattern ? _str_match?(exp_msg, err_msg) : exp_msg == err_msg
+ diff = " Expected Exception(#{exc}) was raised, but the message doesn't match.\n"
+ if type == :pattern
+ diff += " Expected #{exp_msg.inspect} to match #{err_msg.inspect}."
+ else
+ diff += assertion_diff(exp_msg, err_msg)
+ end
+ end
+ assert_true(ret, msg, diff)
+end
+
+def pass
+ assert_true(true)
+end
+
+def flunk(msg = "Epic Fail!", diff = "")
+ assert_true(false, msg, diff)
end
##
# Report the test result and print all assertions
# which were reported broken.
-def report()
+def report
t_print("\n")
$asserts.each do |msg|
- t_print "#{msg}\n"
+ t_print("#{msg}\n")
end
- $total_test = $ok_test+$ko_test+$kill_test
- t_print("Total: #{$total_test}\n")
+ $total_test = $ok_test + $ko_test + $kill_test + $warning_test + $skip_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")
+ t_print(" OK: #{$ok_test}\n")
+ t_print(" KO: #{$ko_test}\n")
+ t_print(" Crash: #{$kill_test}\n")
+ t_print("Warning: #{$warning_test}\n")
+ t_print(" Skip: #{$skip_test}\n")
if Object.const_defined?(:Time)
t_time = Time.now - $test_start
- t_print(" Time: #{t_time.round(2)} seconds\n")
+ t_print(" Time: #{t_time.round(2)} seconds\n")
end
+
+ $ko_test == 0 && $kill_test == 0
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
+def _eval_assertion(meth, exp, act_or_msg, msg, block)
+ if block
+ exp, act, msg = exp, block.call, act_or_msg
else
- true
+ exp, act, msg = exp, act_or_msg, msg
end
+ return exp.__send__(meth, act), exp, act, msg
end
##
# Skip the test
-class MRubyTestSkip < NotImplementedError
- attr_accessor :cause
- def initialize(cause)
- @cause = cause
- end
-end
+class MRubyTestSkip < NotImplementedError; end
def skip(cause = "")
raise MRubyTestSkip.new(cause)
diff --git a/test/bintest.rb b/test/bintest.rb
index b62419d44..a6888c9fb 100644
--- a/test/bintest.rb
+++ b/test/bintest.rb
@@ -1,13 +1,15 @@
$:.unshift File.dirname(File.dirname(File.expand_path(__FILE__)))
require 'test/assert.rb'
+GEMNAME = ""
+
def cmd(s)
- case RbConfig::CONFIG['host_os']
- when /mswin(?!ce)|mingw|bccwin/
- "bin\\#{s}.exe"
- else
- "bin/#{s}"
+ path = s == "mrbc" ? ENV['MRBCFILE'] : "#{ENV['BUILD_DIR']}/bin/#{s}"
+ path = path.sub(/\.exe\z/, "")
+ if /mswin(?!ce)|mingw|bccwin/ =~ RbConfig::CONFIG['host_os']
+ path = "#{path}.exe".tr("/", "\\")
end
+ path
end
def shellquote(s)
@@ -19,6 +21,8 @@ def shellquote(s)
end
end
+print "bintest - Command Binary Test\n\n"
+
ARGV.each do |gem|
case gem
when '-v'; $mrbtest_verbose = true
@@ -30,8 +34,9 @@ ARGV.each do |gem|
end
Dir["#{gem}/bintest/**/*.rb"].each do |file|
+ GEMNAME.replace(File.basename(gem))
load file
end
end
-load 'test/report.rb'
+exit report
diff --git a/test/report.rb b/test/report.rb
deleted file mode 100644
index fb77fd0aa..000000000
--- a/test/report.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-report
-if $ko_test > 0 or $kill_test > 0
- raise "mrbtest failed (KO:#{$ko_test}, Crash:#{$kill_test})"
-end
diff --git a/test/t/argumenterror.rb b/test/t/argumenterror.rb
index abb53429b..3dcb29a4b 100644
--- a/test/t/argumenterror.rb
+++ b/test/t/argumenterror.rb
@@ -1,6 +1,13 @@
##
# ArgumentError ISO Test
+def assert_argnum_error(given, expected, &block)
+ assert("wrong number of arguments") do
+ message = "wrong number of arguments (given #{given}, expected #{expected})"
+ assert_raise_with_message(ArgumentError, message, &block)
+ end
+end
+
assert('ArgumentError', '15.2.24') do
e2 = nil
a = []
@@ -14,3 +21,12 @@ assert('ArgumentError', '15.2.24') do
assert_equal(Class, ArgumentError.class)
assert_equal(ArgumentError, e2.class)
end
+
+assert("'wrong number of arguments' from mrb_get_args") do
+ assert_argnum_error(0, "1+"){__send__}
+ assert_argnum_error(0, 1..2){Object.const_defined?}
+ assert_argnum_error(3, 1..2){Object.const_defined?(:A, true, 2)}
+ assert_argnum_error(2, 0..1){{}.default(1, 2)}
+ assert_argnum_error(1, 2){Object.const_set(:B)}
+ assert_argnum_error(3, 2){Object.const_set(:C, 1, 2)}
+end
diff --git a/test/t/array.rb b/test/t/array.rb
index ecec39363..5f26259f2 100644
--- a/test/t/array.rb
+++ b/test/t/array.rb
@@ -5,7 +5,7 @@ assert('Array', '15.2.12') do
assert_equal(Class, Array.class)
end
-assert('Array inclueded modules', '15.2.12.3') do
+assert('Array included modules', '15.2.12.3') do
assert_true(Array.include?(Enumerable))
end
@@ -55,9 +55,12 @@ assert('Array#[]', '15.2.12.5.4') do
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])
+ assert_equal(["b", "c", "d", "e"], a[1..])
+ assert_equal(["a", "b", "c"], a[..2])
+ skip unless Object.const_defined?(:Float)
+ assert_equal("b", a[1.1])
end
assert('Array#[]=', '15.2.12.5.5') do
@@ -91,6 +94,10 @@ assert('Array#[]=', '15.2.12.5.5') do
a[2...4] = 6
assert_equal([1,2,6,5], a)
+ a = [1,2,3,4,5]
+ a[2...] = 6
+ assert_equal([1,2,6], a)
+
# passing self (#3274)
a = [1,2,3]
a[1,0] = a
@@ -238,7 +245,7 @@ assert('Array#pop', '15.2.12.5.21') do
assert_equal([1,2], a)
assert_equal(3, b)
- assert_raise(RuntimeError) { [].freeze.pop }
+ assert_raise(FrozenError) { [].freeze.pop }
end
assert('Array#push', '15.2.12.5.22') do
@@ -287,7 +294,25 @@ assert('Array#shift', '15.2.12.5.27') do
assert_equal([2,3], a)
assert_equal(1, b)
- assert_raise(RuntimeError) { [].freeze.shift }
+ assert_raise(FrozenError) { [].freeze.shift }
+
+ # Array#shift with argument
+ assert_equal([], [].shift(1))
+
+ a = [1,2,3]
+ b = a.shift(1)
+ assert_equal([2,3], a)
+ assert_equal([1], b)
+
+ a = [1,2,3,4]
+ b = a.shift(3)
+ assert_equal([4], a)
+ assert_equal([1,2,3], b)
+
+ a = [1,2,3]
+ b = a.shift(4)
+ assert_equal([], a)
+ assert_equal([1,2,3], b)
end
assert('Array#size', '15.2.12.5.28') do
@@ -297,11 +322,38 @@ assert('Array#size', '15.2.12.5.28') do
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))
+ a = [*(1..100)]
+ b = a.dup
+
+ assert_equal(1, a.slice(0))
+ assert_equal(100, a.slice(99))
+ assert_nil(a.slice(100))
+ assert_equal(100, a.slice(-1))
+ assert_equal(99, a.slice(-2))
+ assert_equal(1, a.slice(-100))
+ assert_nil(a.slice(-101))
+ assert_equal([1], a.slice(0,1))
+ assert_equal([100], a.slice(99,1))
+ assert_equal([], a.slice(100,1))
+ assert_equal([100], a.slice(99,100))
+ assert_equal([100], a.slice(-1,1))
+ assert_equal([99], a.slice(-2,1))
+ assert_equal([10, 11, 12], a.slice(9, 3))
+ assert_equal([10, 11, 12], a.slice(-91, 3))
+ assert_nil(a.slice(-101, 2))
+ assert_equal([1], a.slice(0..0))
+ assert_equal([100], a.slice(99..99))
+ assert_equal([], a.slice(100..100))
+ assert_equal([100], a.slice(99..200))
+ assert_equal([100], a.slice(-1..-1))
+ assert_equal([99], a.slice(-2..-2))
+ assert_equal([10, 11, 12], a.slice(9..11))
+ assert_equal([10, 11, 12], a.slice(-91..-89))
+ assert_equal([10, 11, 12], a.slice(-91..-89))
+ assert_nil(a.slice(-101..-1))
+ assert_nil(a.slice(10, -3))
+ assert_equal([], a.slice(10..7))
+ assert_equal(b, a)
end
assert('Array#unshift', '15.2.12.5.30') do
@@ -318,11 +370,12 @@ end
assert('Array#to_s', '15.2.12.5.31 / 15.2.12.5.32') do
a = [2, 3, 4, 5]
+ a[4] = a
r1 = a.to_s
r2 = a.inspect
assert_equal(r2, r1)
- assert_equal("[2, 3, 4, 5]", r1)
+ assert_equal("[2, 3, 4, 5, [...]]", r1)
end
assert('Array#==', '15.2.12.5.33') do
@@ -343,8 +396,7 @@ 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_true(a.hash.is_a? Integer)
assert_equal([1,2].hash, [1,2].hash)
end
@@ -360,13 +412,6 @@ 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)
@@ -386,16 +431,15 @@ assert("Array#rindex") do
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
+ assert_raise(FrozenError) 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
diff --git a/test/t/basicobject.rb b/test/t/basicobject.rb
index f33171266..c5aa3f171 100644
--- a/test/t/basicobject.rb
+++ b/test/t/basicobject.rb
@@ -8,4 +8,3 @@ end
assert('BasicObject superclass') do
assert_nil(BasicObject.superclass)
end
-
diff --git a/test/t/bs_block.rb b/test/t/bs_block.rb
index 62eb7e32e..f4e4da375 100644
--- a/test/t/bs_block.rb
+++ b/test/t/bs_block.rb
@@ -408,42 +408,43 @@ assert('BS Block 32') do
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
+ assert_nothing_raised 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 render(&block)
+ "in render"
+ end
end
- def respond
- @response.call
+ 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
+ t.test_for_bug
end
- t = Controller.new
- assert_true t.test_for_bug
end
assert("BS Block 33") do
@@ -519,3 +520,15 @@ assert('BS Block 38') do
assert_equal [1,2,3,4,5], iter{|a,b,c=:c,d,e| [a,b,c,d,e]}
end
+
+assert('BS Block 39') do
+ def iter
+ yield 1
+ end
+
+ assert_equal([1, 2, nil]) do
+ iter{|a, b=2, c|
+ [a, b, c]
+ }
+ end
+end
diff --git a/test/t/bs_literal.rb b/test/t/bs_literal.rb
index c6c38140b..d60957f0b 100644
--- a/test/t/bs_literal.rb
+++ b/test/t/bs_literal.rb
@@ -34,5 +34,5 @@ assert('BS Literal 8') do
end
assert('BS Literal 9') do
- assert_equal Fixnum, 1234.class
+ assert_equal Integer, 1234.class
end
diff --git a/test/t/class.rb b/test/t/class.rb
index 85450f200..1b4b84890 100644
--- a/test/t/class.rb
+++ b/test/t/class.rb
@@ -236,6 +236,11 @@ assert('class to return the last value') do
assert_equal(m, :m)
end
+assert('class to return nil if body is empty') do
+ assert_nil(class C end)
+ assert_nil(class << self; end)
+end
+
assert('raise when superclass is not a class') do
module FirstModule; end
assert_raise(TypeError, 'should raise TypeError') do
@@ -350,7 +355,14 @@ assert('singleton tests') do
7
end
end
- end if class_defined?("Float")
+ end if Object.const_defined?(:Float)
+
+ o = Object.new
+ sc = class << o; self end
+ o.freeze
+ assert_predicate(sc, :frozen?)
+
+ assert_predicate(class << Object.new.freeze; self end, :frozen?)
end
assert('clone Class') do
@@ -360,7 +372,21 @@ assert('clone Class') do
end
end
- Foo.clone.new.func
+ assert_true(Foo.clone.new.func)
+end
+
+assert('class definition in singleton class') do
+ class AClassS
+ class << self
+ class BClass
+ end
+
+ def iclass
+ BClass
+ end
+ end
+ end
+ assert_equal(Class, AClassS.iclass.class)
end
assert('class variable and class << self style class method') do
@@ -428,6 +454,25 @@ assert('overriding class variable with a module (#3235)') do
end
end
+assert('class variable for frozen class/module') do
+ module CVarForFrozenModule
+ freeze
+ assert_raise(FrozenError) { @@cv = 1 }
+ end
+
+ class CVarForFrozenClassA
+ @@a = nil
+ freeze
+ end
+ class CVarForFrozenClassB < CVarForFrozenClassA
+ def a=(v)
+ @@a = v
+ end
+ end
+ b = CVarForFrozenClassB.new
+ assert_raise(FrozenError) { b.a = 1 }
+end
+
assert('class with non-class/module outer raises TypeError') do
assert_raise(TypeError) { class 0::C1; end }
assert_raise(TypeError) { class []::C2; end }
diff --git a/test/t/ensure.rb b/test/t/ensure.rb
index bef397059..bdb5bffc4 100644
--- a/test/t/ensure.rb
+++ b/test/t/ensure.rb
@@ -1,16 +1,16 @@
##
# ensure Test
-assert('ensure - context - yield') do
- class EnsureYieldBreak
- attr_reader :ensure_context
- def try
- yield
- ensure
- @ensure_context = self
- end
+class EnsureYieldBreak
+ attr_reader :ensure_context
+ def try
+ yield
+ ensure
+ @ensure_context = self
end
+end
+assert('ensure - context - yield') do
yielder = EnsureYieldBreak.new
yielder.try do
end
@@ -18,15 +18,6 @@ assert('ensure - context - yield') do
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
@@ -35,15 +26,6 @@ assert('ensure - context - yield and break') do
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
diff --git a/test/t/enumerable.rb b/test/t/enumerable.rb
index 359c3451b..9e7602db7 100644
--- a/test/t/enumerable.rb
+++ b/test/t/enumerable.rb
@@ -45,7 +45,7 @@ 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 }
+ assert_equal 'a', [1,2,3].detect(->{"a"}) { false }
end
assert('Array#each_with_index', '15.3.2.2.5') do
@@ -64,11 +64,11 @@ 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 }
+ 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]
+ assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0}
end
assert('Enumerable#grep', '15.3.2.2.9') do
diff --git a/test/t/exception.rb b/test/t/exception.rb
index ce7b5841e..6f517a5c3 100644
--- a/test/t/exception.rb
+++ b/test/t/exception.rb
@@ -263,10 +263,10 @@ assert('Exception 13') do
end
assert('Exception 14') do
- def exception_test14; UnknownConstant; end
+ def (o = Object.new).exception_test14; UnknownConstant end
a = :ng
begin
- send(:exception_test14)
+ o.__send__(:exception_test14)
rescue
a = :ok
end
@@ -352,8 +352,10 @@ assert('Exception 19') do
assert_equal [true, true], Class4Exception19.new.a
end
-assert('Exception#inspect without message') do
+assert('Exception#inspect') do
assert_equal "Exception", Exception.new.inspect
+ assert_equal "Exception", Exception.new("").inspect
+ assert_equal "error! (Exception)", Exception.new("error!").inspect
end
assert('Exception#backtrace') do
@@ -380,7 +382,8 @@ def backtrace_available?
begin
raise "XXX"
rescue => exception
- not exception.backtrace.empty?
+ return false if exception.backtrace.empty?
+ not exception.backtrace[0].include?("unknown")
end
end
@@ -398,7 +401,7 @@ assert('GC in rescue') do
end
rescue => exception
GC.start
- assert_equal("#{__FILE__}:#{line}:in call",
+ assert_equal("#{__FILE__}:#{line}",
exception.backtrace.first)
end
end
@@ -416,7 +419,7 @@ assert('Method call in rescue') do
rescue => exception
[3].each do
end
- assert_equal("#{__FILE__}:#{line}:in call",
+ assert_equal("#{__FILE__}:#{line}",
exception.backtrace.first)
end
end
diff --git a/test/t/float.rb b/test/t/float.rb
index 92f7a15f1..f6f6d01dd 100644
--- a/test/t/float.rb
+++ b/test/t/float.rb
@@ -1,7 +1,7 @@
##
# Float ISO Test
-if class_defined?("Float")
+if Object.const_defined?(:Float)
assert('Float', '15.2.9') do
assert_equal Class, Float.class
@@ -35,11 +35,66 @@ assert('Float#*', '15.2.9.3.3') do
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)
+ assert_float(1.0, 3.123456789 / 3.123456789)
+ assert_float(3.123456789, 3.123456789 / 1)
+ assert_float(2.875, -5.75 / -2.0)
+ assert_float(-2.875, 5.75 / -2)
+ assert_float(-2.875, -5.75 / 2.0)
+ assert_float(Float::NAN, 0.0 / 0.0)
+ assert_float(Float::NAN, -0.0 / -0.0)
+ assert_float(Float::NAN, -0.0 / 0.0)
+ assert_float(Float::NAN, Float::NAN / Float::NAN)
+ assert_float(Float::NAN, Float::NAN / 0.0)
+ assert_float(Float::NAN, Float::NAN / -0.0)
+ assert_float(Float::NAN, Float::NAN / 2.0)
+ assert_float(Float::NAN, Float::NAN / -2.0)
+ assert_float(Float::NAN, 0.0 / Float::NAN)
+ assert_float(Float::NAN, -0.0 / Float::NAN)
+ assert_float(Float::NAN, 2.0 / Float::NAN)
+ assert_float(Float::NAN, -2.0 / Float::NAN)
+ assert_float(Float::NAN, Float::INFINITY / Float::INFINITY)
+ assert_float(Float::NAN, -Float::INFINITY / Float::INFINITY)
+ assert_float(Float::NAN, Float::INFINITY / -Float::INFINITY)
+ assert_float(Float::NAN, -Float::INFINITY / -Float::INFINITY)
+ assert_float(Float::INFINITY, 1.0 / 0.0)
+ assert_float(Float::INFINITY, -1.0 / -0.0)
+ assert_float(-Float::INFINITY, 1.0 / -0.0)
+ assert_float(-Float::INFINITY, -1.0 / 0.0)
+ assert_float(0.0, 1.0 / Float::INFINITY)
+ assert_float(0.0, -1.0 / -Float::INFINITY)
+ assert_float(-0.0, -1.0 / Float::INFINITY)
+ assert_float(-0.0, 1.0 / -Float::INFINITY)
+end
+assert('Float#quo') do
+ assert_float(1.0, 3.123456789.quo(3.123456789))
+ assert_float(3.123456789, 3.123456789.quo(1))
+ assert_float(2.875, -5.75.quo(-2.0))
+ assert_float(-2.875, 5.75.quo(-2))
+ assert_float(-2.875, -5.75.quo(2.0))
+ assert_float(Float::NAN, 0.0.quo(0.0))
+ assert_float(Float::NAN, -0.0.quo(-0.0))
+ assert_float(Float::NAN, -0.0.quo(0.0))
+ assert_float(Float::NAN, Float::NAN.quo(Float::NAN))
+ assert_float(Float::NAN, Float::NAN.quo(0.0))
+ assert_float(Float::NAN, Float::NAN.quo(-0.0))
+ assert_float(Float::NAN, Float::NAN.quo(2.0))
+ assert_float(Float::NAN, Float::NAN.quo(-2.0))
+ assert_float(Float::NAN, 0.0.quo(Float::NAN))
+ assert_float(Float::NAN, -0.0.quo(Float::NAN))
+ assert_float(Float::NAN, 2.0.quo(Float::NAN))
+ assert_float(Float::NAN, -2.0.quo(Float::NAN))
+ assert_float(Float::NAN, Float::INFINITY.quo(Float::INFINITY))
+ assert_float(Float::NAN, -Float::INFINITY.quo(Float::INFINITY))
+ assert_float(Float::NAN, Float::INFINITY.quo(-Float::INFINITY))
+ assert_float(Float::NAN, -Float::INFINITY.quo(-Float::INFINITY))
+ assert_float(Float::INFINITY, 1.0.quo(0.0))
+ assert_float(Float::INFINITY, -1.0.quo(-0.0))
+ assert_float(-Float::INFINITY, 1.0.quo(-0.0))
+ assert_float(-Float::INFINITY, -1.0.quo(0.0))
+ assert_float(0.0, 1.0.quo(Float::INFINITY))
+ assert_float(0.0, -1.0.quo(-Float::INFINITY))
+ assert_float(-0.0, -1.0.quo(Float::INFINITY))
+ assert_float(-0.0, 1.0.quo(-Float::INFINITY))
end
assert('Float#%', '15.2.9.3.5') do
@@ -82,8 +137,8 @@ assert('Float#ceil', '15.2.9.3.8') do
end
assert('Float#finite?', '15.2.9.3.9') do
- assert_true 3.123456789.finite?
- assert_false (1.0 / 0.0).finite?
+ assert_predicate 3.123456789, :finite?
+ assert_not_predicate 1.0 / 0.0, :finite?
end
assert('Float#floor', '15.2.9.3.10') do
@@ -139,7 +194,7 @@ assert('Float#round', '15.2.9.3.12') do
nan = 0.0/0.0
assert_raise(FloatDomainError){ nan.round }
assert_raise(FloatDomainError){ nan.round(-1) }
- assert_true(nan.round(1).nan?)
+ assert_predicate(nan.round(1), :nan?)
end
assert('Float#to_f', '15.2.9.3.13') do
@@ -178,10 +233,10 @@ assert('Float#divmod') do
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?
+ assert_predicate(0.0/0.0, :nan?)
+ assert_not_predicate(0.0, :nan?)
+ assert_not_predicate(1.0/0.0, :nan?)
+ assert_not_predicate(-1.0/0.0, :nan?)
end
assert('Float#<<') do
@@ -206,4 +261,48 @@ assert('Float#>>') do
assert_equal(-1, -23.0 >> 128)
end
-end # class_defined?("Float")
+assert('Float#to_s') do
+ uses_float = 4e38.infinite? # enable MRB_USE_FLOAT32?
+
+ assert_equal("Infinity", Float::INFINITY.to_s)
+ assert_equal("-Infinity", (-Float::INFINITY).to_s)
+ assert_equal("NaN", Float::NAN.to_s)
+ assert_equal("0.0", 0.0.to_s)
+ assert_equal("-0.0", -0.0.to_s)
+ assert_equal("-3.25", -3.25.to_s)
+ assert_equal("50.0", 50.0.to_s)
+ assert_equal("0.0125", 0.0125.to_s)
+ assert_equal("-0.0125", -0.0125.to_s)
+ assert_equal("1.0e-10", 0.0000000001.to_s)
+ assert_equal("-1.0e-10", -0.0000000001.to_s)
+ assert_equal("1.0e+20", 1e20.to_s)
+ assert_equal("-1.0e+20", -1e20.to_s)
+ assert_equal("1.0e+16", 10000000000000000.0.to_s)
+ assert_equal("-1.0e+16", -10000000000000000.0.to_s)
+ assert_equal("100000.0", 100000.0.to_s)
+ assert_equal("-100000.0", -100000.0.to_s)
+ if uses_float
+ assert_equal("1.0e+08", 100000000.0.to_s)
+ assert_equal("-1.0e+08", -100000000.0.to_s)
+ assert_equal("1.0e+07", 10000000.0.to_s)
+ assert_equal("-1.0e+07", -10000000.0.to_s)
+ else
+ assert_equal("1.0e+15", 1000000000000000.0.to_s)
+ assert_equal("-1.0e+15", -1000000000000000.0.to_s)
+ assert_equal("100000000000000.0", 100000000000000.0.to_s)
+ assert_equal("-100000000000000.0", -100000000000000.0.to_s)
+ end
+end
+
+assert('Float#inspect') do
+ assert_equal("-3.25", -3.25.inspect)
+ assert_equal("50.0", 50.0.inspect)
+end
+
+assert('Float#eql?') do
+ assert_operator(5.0, :eql?, 5.0)
+ assert_not_operator(5.0, :eql?, 5)
+ assert_not_operator(5.0, :eql?, "5.0")
+end
+
+end # const_defined?(:Float)
diff --git a/test/t/hash.rb b/test/t/hash.rb
index 8088bfa21..9bc2668ae 100644
--- a/test/t/hash.rb
+++ b/test/t/hash.rb
@@ -1,375 +1,1008 @@
##
# Hash ISO Test
-assert('Hash', '15.2.13') do
- assert_equal Class, Hash.class
-end
+class HashKey
+ attr_accessor :value, :error, :callback
-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
+ self.class.alias_method :[], :new
-assert('Hash#[]', '15.2.13.4.2') do
- a = { 'abc' => 'abc' }
+ def initialize(value, error: nil, callback: nil)
+ @value = value
+ @error = error
+ @callback = callback
+ end
- assert_equal 'abc', a['abc']
+ def ==(other)
+ @callback.(:==, self, other) if @callback
+ return raise_error(:==) if @error == true || @error == :==
+ other.kind_of?(self.class) && @value == other.value
+ end
- # Hash#[] should call #default (#3272)
- hash = {}
- def hash.default(k); self[k] = 1; end
- hash[:foo] += 1
+ def eql?(other)
+ @callback.(:eql?, self, other) if @callback
+ return raise_error(:eql?) if @error == true || @error == :eql?
+ other.kind_of?(self.class) && @value.eql?(other.value)
+ end
- assert_equal 2, hash[:foo]
-end
+ def hash
+ @callback.(:hash, self) if @callback
+ return raise_error(:hash) if @error == true || @error == :hash
+ @value % 3
+ end
-assert('Hash#[]=', '15.2.13.4.3') do
- a = Hash.new
- a['abc'] = 'abc'
+ def to_s
+ "#{self.class}[#{@value}]"
+ end
+ alias inspect to_s
- assert_equal 'abc', a['abc']
+ def raise_error(name)
+ raise "##{self}: #{name} error"
+ end
end
-assert('Hash#clear', '15.2.13.4.4') do
- a = { 'abc' => 'abc' }
- a.clear
-
- assert_equal({ }, a)
+class HashEntries < Array
+ self.class.alias_method :[], :new
+
+ def initialize(entries) self.replace(entries) end
+ def key(index, k=get=true) get ? self[index][0] : (self[index][0] = k) end
+ def value(index, v=get=true) get ? self[index][1] : (self[index][1] = v) end
+ def keys; map{|k, v| k} end
+ def values; map{|k, v| v} end
+ def each_key(&block) each{|k, v| block.(k)} end
+ def each_value(&block) each{|k, v| block.(v)} end
+ def dup2; self.class[*map{|k, v| [k.dup, v.dup]}] end
+ def to_s; "#{self.class}#{super}" end
+ alias inspect to_s
+
+ def hash_for(hash={}, &block)
+ each{|k, v| hash[k] = v}
+ block.(hash) if block
+ hash
+ end
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"])
+def ar_entries
+ HashEntries[
+ [1, "one"],
+ [HashKey[2], :two],
+ [nil, :two],
+ [:one, 1],
+ ["&", "&amp;"],
+ [HashKey[6], :six],
+ [HashKey[5], :five], # same hash code as HashKey[2]
+ ]
+end
+
+def ht_entries
+ ar_entries.dup.push(
+ ["id", 32],
+ [:date, "2020-05-02"],
+ [200, "OK"],
+ ["modifiers", ["left_shift", "control"]],
+ [:banana, :yellow],
+ ["JSON", "JavaScript Object Notation"],
+ [:size, :large],
+ ["key_code", "h"],
+ ["h", 0x04],
+ [[3, 2, 1], "three, two, one"],
+ [:auto, true],
+ [HashKey[12], "December"],
+ [:path, "/path/to/file"],
+ [:name, "Ruby"],
+ )
+end
+
+def merge_entries!(entries1, entries2)
+ entries2.each do |k2, v2|
+ entry1 = entries1.find{|k1, _| k1.eql?(k2)}
+ entry1 ? (entry1[1] = v2) : (entries1 << [k2, v2])
+ end
+ entries1
+end
+
+def product(*arrays, &block)
+ sizes = Array.new(arrays.size+1, 1)
+ (arrays.size-1).downto(0){|i| sizes[i] = arrays[i].size * sizes[i+1]}
+ size = sizes[0]
+ results = Array.new(size){[]}
+ arrays.each_with_index do |array, arrays_i|
+ results_i = -1
+ (size / sizes[arrays_i]).times do
+ array.each do |v|
+ sizes[arrays_i+1].times{results[results_i+=1] << v}
+ end
+ end
+ end
+ results.each{block.(_1)}
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')
+def assert_iterator(exp, obj, meth)
+ params = []
+ obj.__send__(meth) {|param| params << param}
+ assert_equal(exp, params)
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']
+def assert_nothing_crashed(&block)
+ block.call rescue nil
+ pass
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
+assert('Hash', '15.2.13') do
+ assert_equal(Class, Hash.class)
+end
+
+[[:==, '15.2.13.4.1'], [:eql?, '']].each do |meth, iso|
+ assert("Hash##{meth}", iso) do
+ cls = Class.new(Hash){attr_accessor :foo}
+ [ar_entries, ht_entries].each do |entries|
+ h1 = entries.hash_for
+ h2 = entries.dup.reverse!.hash_for
+ assert_operator(h1, meth, h2)
+ assert_operator(h1, meth, h1)
+ assert_not_operator(h1, meth, true)
+ assert_operator({}, meth, Hash.new)
+
+ h1 = entries.hash_for(cls.new(1)) {|h| h.foo = 1}
+ h2 = entries.hash_for(cls.new(2)) {|h| h.foo = 2}
+ assert_operator(h1, meth, h2)
+
+ h1 = entries.hash_for
+ h2 = entries.hash_for(cls.new)
+ assert_operator(h1, meth, h2)
+
+ h1 = (entries.dup << [:_k, 1]).hash_for
+ h2 = (entries.dup << [:_k, 2]).hash_for
+ assert_not_operator(h1, meth, h2)
+
+ h1 = (entries.dup << [:_k1, 0]).hash_for
+ h2 = (entries.dup << [:_k2, 0]).hash_for
+ assert_not_operator(h1, meth, h2)
+
+ h1 = entries.hash_for
+ h2 = (entries.dup << [:_k, 2]).hash_for
+ assert_not_operator(h1, meth, h2)
+
+ k1, v1 = HashKey[-1], HashKey[-2]
+ k2, v2 = HashKey[-1], HashKey[-2]
+ h1 = (entries.dup << [k1, v1]).hash_for
+ h2 = (entries.dup << [k2, v2]).hash_for
+ product([h1, h2], [k1, k2], %i[eql? hash]) do |h, k, m|
+ [k1, k2].each{_1.callback = nil}
+ k.callback = ->(name, *){h.clear if name == m}
+ assert_nothing_crashed{h1.__send__(meth, h2)}
+ end
+ product([h1, h2], [v1, v2]) do |h, v|
+ [v1, v2].each{_1.callback = nil}
+ v.callback = ->(name, *){h.clear if name == meth}
+ assert_nothing_crashed{h1.__send__(meth, h2)}
+ end
+
+ if Object.const_defined?(:Float)
+ h1 = (entries.dup << [-1, true]).hash_for
+ h2 = (entries.dup << [-1.0, true]).hash_for
+ assert_not_operator(h1, meth, h2)
+ h1 = (entries.dup << [-1.0, true]).hash_for
+ h2 = (entries.dup << [-1, true]).hash_for
+ assert_not_operator(h1, meth, h2)
+
+ h1 = (entries.dup << [:_k, 1]).hash_for
+ h2 = (entries.dup << [:_k, 1.0]).hash_for
+ if meth == :==
+ assert_operator(h1, meth, h2)
+ else
+ assert_not_operator(h1, meth, h2)
+ end
+ end
+ end
+ end
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
+assert('Hash#[]', '15.2.13.4.2') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for
+ assert_equal(entries.size, h.size)
+ entries.each{|k, v| assert_equal(v, h[k])}
+ assert_equal(nil, h["_not_found_"])
+ assert_equal(nil, h[:_not_dound_])
+ assert_equal(nil, h[-2])
+
+ k = HashKey[-4]
+ h[HashKey[-1]] = -1
+ h[k] = -4
+ h.delete(k)
+ assert_equal(nil, h[k])
+
+ if Object.const_defined?(:Float)
+ h[-2] = 22
+ assert_equal(nil, h[-2.0])
+ h[-3.0] = 33
+ assert_equal(nil, h[-3])
+ assert_equal(33, h[-3.0])
+ end
+
+ k = HashKey[-2]
+ k.callback = ->(name, *){h.clear if name == :eql?}
+ assert_nothing_crashed{h[k]}
+ k.callback = ->(name, *){h.clear if name == :hash}
+ assert_nothing_crashed{h[k]}
end
- assert_nil a.delete('cba')
- assert_false a.has_key?('abc')
- assert_false b_tmp_1
- assert_true b_tmp_2
+ # Hash#[] should call #default (#3272)
+ h = {}
+ def h.default(k); self[k] = 1; end
+ h[:foo] += 1
+ assert_equal(2, h[:foo])
+end
+
+[%w[[]= 3], %w[store 26]].each do |meth, no|
+ assert("Hash##{meth}", "15.2.13.4.#{no}") do
+ [{}, ht_entries.hash_for].each do |h|
+ # duplicated key
+ k = :_dup_key
+ h.__send__(meth, k, 1)
+ size = h.size
+ h.__send__(meth, k, 2)
+ assert_equal(size, h.size)
+ assert_equal(2, h[k])
+
+ # freeze string key
+ k = "_mutable"
+ h.__send__(meth, k, 1)
+ h_k = h.keys[-1]
+ assert_not_same(k, h_k)
+ assert_predicate(h_k, :frozen?)
+ assert_not_predicate(k, :frozen?)
+
+ # frozen string key
+ k = "_immutable".freeze
+ h.__send__(meth, k, 2)
+ h_k = h.keys[-1]
+ assert_same(k, h_k)
+ assert_predicate(h_k, :frozen?)
+
+ # numeric key
+ if Object.const_defined?(:Float)
+ h.__send__(meth, 3, :fixnum)
+ h.__send__(meth, 3.0, :float)
+ assert_equal(:fixnum, h[3])
+ assert_equal(:float, h[3.0])
+ h.__send__(meth, 4.0, :float)
+ h.__send__(meth, 4, :fixnum)
+ assert_equal(:fixnum, h[4])
+ assert_equal(:float, h[4.0])
+ end
+
+ # other key
+ k = [:_array]
+ h.__send__(meth, k, :_array)
+ h_k = h.keys[-1]
+ assert_same(k, h_k)
+ assert_not_predicate(h_k, :frozen?)
+ assert_not_predicate(k, :frozen?)
+
+ # deleted key
+ k1, k2, k3 = HashKey[-1], HashKey[-4], HashKey[-7] # same hash code
+ h.__send__(meth, k1, 1)
+ h.__send__(meth, k2, -4)
+ h.__send__(meth, k3, 73)
+ size = h.size
+ h.delete(k1)
+ h.delete(k2)
+ h.__send__(meth, k2, 40)
+ assert_equal(nil, h[k1])
+ assert_equal(40, h[k2])
+ assert_equal(73, h[k3])
+ assert_equal(size - 1, h.size)
+
+ # frozen
+ h.freeze
+ assert_raise(FrozenError){h.__send__(meth, -100, 1)}
+ end
+
+ [ar_entries.hash_for, ht_entries.hash_for].each do |h|
+ k = HashKey[-2]
+ k.callback = ->(name, *){h.clear if name == :eql?}
+ assert_nothing_crashed{h.__send__(meth, k, 2)}
+ k.callback = ->(name, *){h.clear if name == :hash}
+ assert_nothing_crashed{h.__send__(meth, k, 2)}
+ end
+ end
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
+assert('Hash#clear', '15.2.13.4.4') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for
+ assert_same(h, h.clear)
+ assert_equal(0, h.size)
+ assert_nil(h[entries.key(3)])
+
+ h.freeze
+ assert_raise(FrozenError){h.clear}
end
- assert_equal 'abc_key', key
- assert_equal 'abc_value', value
+ h = {}.freeze
+ assert_raise(FrozenError){h.clear}
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
+assert('Hash#dup') do
+ cls = Class.new(Hash){attr_accessor :foo}
+ [ar_entries, ht_entries].each do |entries|
+ h1 = entries.hash_for(cls.new(61)){|h| h.foo = 23}.freeze
+ h2 = h1.dup
+ assert_not_predicate(h2, :frozen?)
+ assert_equal(h1.class, h2.class)
+ assert_equal(entries, h2.to_a)
+ assert_equal(23, h2.foo)
+ assert_equal(61, h2["_not_found_"])
+ h2[-10] = 10
+ assert_equal(10, h2[-10])
+ assert_not_operator(h1, :key?, -10)
+
+ h = entries.hash_for
+ k = HashKey[-1]
+ h[k] = 1
+ k.callback = ->(*){h.clear}
+ assert_nothing_crashed{h.dup}
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
+assert('Hash#default', '15.2.13.4.5') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for(Hash.new)
+ assert_equal(nil, h.default)
+ assert_equal(nil, h.default(-2))
+
+ h = entries.hash_for(Hash.new(-88))
+ assert_equal(-88, h.default)
+ assert_equal(-88, h.default(-2))
+ assert_not_operator(h, :key?, -2)
+ assert_raise(ArgumentError){h.default(-2,-2)}
+
+ proc = ->(h, k){h[k] = k * 3}
+ h = entries.hash_for(Hash.new(proc))
+ assert_equal(proc, h.default(-2))
+
+ h = entries.hash_for(Hash.new(&proc))
+ assert_equal(nil, h.default)
+ assert_not_operator(h, :key?, -2)
+ assert_equal(-6, h.default(-2))
+ assert_equal(-6, h[-2])
+ h[-2] = -5
+ assert_equal(-6, h.default(-2))
+ assert_equal(-6, h[-2])
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#default=', '15.2.13.4.6') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for(Hash.new)
+ h.default = 3
+ assert_equal(3, h[-2])
+ assert_equal(entries.value(0), h[entries.key(0)])
-assert('Hash#has_key?', '15.2.13.4.13') do
- a = { 'abc_key' => 'abc_value' }
- b = Hash.new
+ h.default = 4
+ assert_equal(4, h[-2])
- assert_true a.has_key?('abc_key')
- assert_false b.has_key?('cba')
-end
+ h.default = nil
+ assert_equal(nil, h[-2])
-assert('Hash#has_value?', '15.2.13.4.14') do
- a = { 'abc_key' => 'abc_value' }
- b = Hash.new
+ h.default = [5]
+ assert_same(h[-2], h[-3])
- assert_true a.has_value?('abc_value')
- assert_false b.has_value?('cba')
+ h.freeze
+ assert_raise(FrozenError){h.default = 3}
+ end
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')
+assert('Hash#default_proc', '15.2.13.4.7') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for({})
+ assert_nil(h.default_proc)
+
+ h = entries.hash_for(Hash.new(34))
+ assert_nil(h.default_proc)
+
+ h = entries.hash_for(Hash.new{|h, k| h[k] = k * 3})
+ proc = h.default_proc
+ assert_equal(Proc, proc.class)
+ assert_equal(6, proc.(h, 2))
+ assert_equal([2, 6], h.to_a[-1])
+ end
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#delete', '15.2.13.4.8') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for
+ pairs = entries.dup
+ [0, 2, -1].each do |i|
+ k, v = pairs.delete_at(i)
+ assert_equal(v, h.delete(k))
+ assert_equal(nil, h[k])
+ assert_equal(false, h.key?(k))
+ end
+ [entries.key(0), "_not_found_"].each {|k|assert_equal(nil, h.delete(k))}
+ assert_equal(pairs.size, h.size)
+ assert_equal(pairs, h.to_a)
+ pairs.each {|k, v| assert_equal(v, h[k])}
+
+ h = entries.hash_for
+ pairs = entries.dup
+ [pairs.delete_at(1), ["_not_found_", "_default"]].each do |k, v|
+ assert_equal(v, h.delete(k){"_default"})
+ assert_equal(nil, h[k])
+ assert_equal(false, h.key?(k))
+ end
+ assert_equal(pairs.size, h.size)
+ assert_equal(pairs, h.to_a)
+ pairs.each {|k, v| assert_equal(v, h[k])}
+
+ if Object.const_defined?(:Float)
+ h = entries.dup.push([-5, 1], [-5.0, 2], [-6.0, 3], [-6, 4]).hash_for
+ assert_equal(1, h.delete(-5))
+ assert_equal(3, h.delete(-6.0))
+ end
+
+ # nil value with block
+ h = entries.hash_for
+ k = "_nil"
+ h[k] = nil
+ assert_equal(nil, h.delete(k){"blk"})
+ assert_equal(false, h.key?(k))
+
+ k = HashKey[-31, callback: ->(*){h.clear}]
+ assert_nothing_crashed{h.delete(k)}
+ end
-assert('Hash#initialize_copy', '15.2.13.4.17') do
- a = { 'abc_key' => 'abc_value' }
- b = Hash.new.initialize_copy(a)
+ assert_raise(ArgumentError){{}.delete}
+ assert_raise(ArgumentError){{}.delete(1,2)}
- assert_equal({ 'abc_key' => 'abc_value' }, b)
+ h = {}.freeze
+ assert_raise(FrozenError){h.delete(1)}
+end
+
+[%w[each 9], %w[each_key 10], %w[each_value 11]].each do |meth, no|
+ assert("Hash##{meth}", "15.2.13.4.#{no}") do
+ [ar_entries, ht_entries].each do |entries|
+ exp = []
+ entries.__send__(meth){|param| exp << param}
+ assert_iterator(exp, entries.hash_for, meth)
+
+ h = entries.hash_for
+ entries.shift
+ h.shift
+ entry = entries.delete_at(1)
+ h.delete(entry[0])
+ h.delete(entries.delete_at(-4)[0])
+ entries << entry
+ h.store(*entry)
+ exp = []
+ entries.__send__(meth){|param| exp << param}
+ assert_iterator(exp, h, meth)
+ end
+
+ assert_iterator([], {}, meth)
+ end
end
-assert('Hash#key?', '15.2.13.4.18') do
- a = { 'abc_key' => 'abc_value' }
- b = Hash.new
+assert('Hash#empty?', '15.2.13.4.12') do
+ [ar_entries, ht_entries].each do |entries|
+ assert_not_predicate entries.hash_for, :empty?
- assert_true a.key?('abc_key')
- assert_false b.key?('cba')
-end
+ h = entries.hash_for
+ h.shift
+ h.delete(entries.key(-1))
+ assert_not_predicate h, :empty?
-assert('Hash#keys', '15.2.13.4.19') do
- a = { 'abc_key' => 'abc_value' }
+ h = entries.hash_for
+ entries.size.times{h.shift}
+ assert_predicate(h, :empty?)
- assert_equal ['abc_key'], a.keys
-end
-
-assert('Hash#length', '15.2.13.4.20') do
- a = { 'abc_key' => 'abc_value' }
- b = Hash.new
+ h = entries.hash_for
+ entries.each {|k, v| h.delete(k)}
+ assert_predicate(h, :empty?)
+ end
- assert_equal 1, a.length
- assert_equal 0, b.length
+ assert_predicate(Hash.new, :empty?)
+ assert_predicate(Hash.new(1), :empty?)
+ assert_predicate(Hash.new{|h, k| h[k] = 2}, :empty?)
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
+[%w[has_key? 13], %w[include? 15], %w[key? 18], %w[member? 21]].each do |meth,no|
+ assert("Hash##{meth}", "15.2.13.4.#{no}") do
+ [ar_entries, ht_entries].each do |entries|
+ pairs = entries.dup.push([HashKey[-3], 3], [nil, "NIL"])
+ h = pairs.hash_for
+ pairs.each{|k, v| assert_operator(h, meth, k)}
+ assert_not_operator(h, meth, HashKey[-6])
+ assert_not_operator(h, meth, 3)
-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' }
+ if Object.const_defined?(:Float)
+ hh = entries.push([-7, :i], [-8.0, :f]).hash_for
+ assert_not_operator(hh, meth, -7.0)
+ assert_not_operator(hh, meth, -8)
+ assert_operator(hh, meth, -8.0)
+ end
- result_1 = a.merge b
- result_2 = a.merge(b) do |key, original, new|
- original
- end
+ h.shift
+ assert_not_operator(h, meth, pairs.key(0))
- 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)
+ h.delete(pairs.key(3))
+ assert_not_operator(h, meth, pairs.key(3))
- assert_raise(TypeError) do
- { 'abc_key' => 'abc_value' }.merge "a"
+ k = HashKey[-31, callback: ->(*){h.clear}]
+ assert_nothing_crashed{h.__send__(meth, k)}
+ end
end
-end
-assert('Hash#replace', '15.2.13.4.23') do
- a = { 'abc_key' => 'abc_value' }
- b = Hash.new.replace(a)
+ h = Hash.new{|h, k| h[1] = 1}
+ assert_not_operator(h, meth, 1)
+end
- assert_equal({ 'abc_key' => 'abc_value' }, b)
+[%w[has_value? 14], %w[value? 24]].each do |meth, no|
+ assert("Hash##{meth}", "15.2.13.4.#{no}") do
+ [ar_entries, ht_entries].each do |entries|
+ entries.push([HashKey[-5], -8], ["NIL", nil])
+ h = entries.hash_for
+ entries.each{|k, v| assert_operator(h, meth, v)}
+ assert_operator(h, meth, -8.0) if Object.const_defined?(:Float)
+ assert_not_operator(h, meth, "-8")
- a = Hash.new(42)
- b = {}
- b.replace(a)
- assert_equal(42, b[1])
+ h.shift
+ assert_not_operator(h, meth, entries.value(0))
- a = Hash.new{|h,x| x}
- b.replace(a)
- assert_equal(127, b[127])
+ h.delete(entries.key(3))
+ assert_not_operator(h, meth, entries.value(3))
- assert_raise(TypeError) do
- { 'abc_key' => 'abc_value' }.replace "a"
+ v = HashKey[-31, callback: ->(*){h.clear}]
+ assert_nothing_crashed{h.__send__(meth, v)}
+ end
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
+ h = Hash.new{|h, k| h[1] = 1}
+ assert_not_operator(h, meth, 1)
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
+assert('Hash#initialize', '15.2.13.4.16') do
+ h = Hash.new
+ assert_equal(Hash, h.class)
+ assert_not_operator(h, :key?, 1)
+ assert_equal(nil, h[1])
+
+ h = Hash.new([8])
+ assert_not_operator(h, :key?, 1)
+ assert_equal([8], h[1])
+ assert_same(h[1], h[2])
+
+ k = "key"
+ h = Hash.new{|hash, key| [hash, key]}
+ assert_not_operator(h, :key?, k)
+ assert_equal([h, k], h[k])
+ assert_same(h, h[k][0])
+ assert_same(k, h[k][1])
+
+ assert_raise(ArgumentError){Hash.new(1,2)}
+ assert_raise(ArgumentError){Hash.new(1){}}
+end
+
+[%w[keys 19], %w[values 28]].each do |meth, no|
+ assert("Hash##{meth}", "15.2.13.4.#{no}") do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for
+ assert_equal(entries.__send__(meth), h.__send__(meth))
+
+ h.shift
+ entries.shift
+ h.delete(entries.delete_at(3)[0])
+ assert_equal(entries.__send__(meth), h.__send__(meth))
+ end
+
+ assert_equal([], {}.__send__(meth))
+ end
end
-assert('Hash#store', '15.2.13.4.26') do
- a = Hash.new
- a.store('abc', 'abc')
+[%w[length 20], %w[size 25]].each do |meth, no|
+ assert("Hash##{meth}", "15.2.13.4.#{no}") do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for
+ assert_equal(entries.size, h.__send__(meth))
+
+ h.shift
+ entries.shift
+ h.delete(entries.delete_at(3)[0])
+ assert_equal(entries.size, h.__send__(meth))
+ end
- assert_equal 'abc', a['abc']
+ assert_equal(0, Hash.new.__send__(meth))
+ end
end
-assert('Hash#value?', '15.2.13.4.27') do
- a = { 'abc_key' => 'abc_value' }
- b = Hash.new
+assert('Hash#merge', '15.2.13.4.22') do
+ cls = Class.new(Hash){attr_accessor :foo}
+ ar_pairs = HashEntries[
+ ["id", 32],
+ [nil, :two],
+ ["&", "&amp;"],
+ [:same_key, :AR],
+ [HashKey[2], 20],
+ ]
+ ht_pairs = HashEntries[
+ *(1..20).map{[_1, _1.to_s]},
+ [:same_key, :HT],
+ [:age, 32],
+ [HashKey[5], 500],
+ ]
+
+ [[ar_pairs, ht_pairs], [ht_pairs, ar_pairs]].each do |entries1, entries2|
+ h1 = entries1.hash_for(cls.new(:dv1)){|h| h.foo = :iv1}.freeze
+ h2 = entries2.hash_for(Hash.new(:dv2)).freeze
+ h3 = h1.merge(h2)
+ assert_equal(entries1, h1.to_a)
+ assert_equal(merge_entries!(entries1.dup2, entries2), h3.to_a)
+ assert_equal(cls, h3.class)
+ assert_equal(:dv1, h3.default)
+ assert_equal(:iv1, h3.foo)
+
+ h3 = {}.merge(entries2.hash_for(cls.new))
+ assert_equal(merge_entries!([], entries2), h3.to_a)
+ assert_equal(Hash, h3.class)
+
+ h3 = entries1.hash_for.merge({})
+ assert_equal(merge_entries!(entries1.dup2, []), h3.to_a)
+
+ h1 = entries1.hash_for
+ h2 = entries2.hash_for
+ h3 = h1.merge(h2){|k, v1, v2| [k, v1, v2]}
+ exp = merge_entries!(entries1.dup2, entries2)
+ exp.find{|k, _| k == :same_key}[1] = [
+ :same_key,
+ entries1.find{|k, _| k == :same_key}[1],
+ entries2.find{|k, _| k == :same_key}[1],
+ ]
+ assert_equal(exp, h3.to_a)
+
+ assert_raise(TypeError){entries1.hash_for.merge("str")}
+
+ k2 = HashKey[-2]
+ entries2 << [k2, 234]
+ h1, h2 = entries1.hash_for, entries2.hash_for
+ k2.callback = ->(name, *){h1.clear if name == :eql?}
+ assert_nothing_crashed{h1.merge(h2)}
+ h1, h2 = entries1.hash_for, entries2.hash_for
+ k2.callback = ->(name, *){h2.clear if name == :eql?}
+ assert_nothing_crashed{h1.merge(h2)}
+ h1, h2 = entries1.hash_for, entries2.hash_for
+ k2.callback = ->(name, *){h1.clear if name == :hash}
+ assert_nothing_crashed{h1.merge(h2)}
+ h1, h2 = entries1.hash_for, entries2.hash_for
+ k2.callback = ->(name, *){h2.clear if name == :hash}
+ assert_nothing_crashed{h1.merge(h2)}
+ end
+end
- assert_true a.value?('abc_value')
- assert_false b.value?('cba')
+assert("Hash#replace", "15.2.13.4.23") do
+ cls = Class.new(Hash){attr_accessor :foo}
+ e = [ar_entries, ht_entries]
+ [e, e.reverse].each do |entries1, entries2|
+ h1 = entries1.hash_for
+ assert_same(h1, h1.replace(h1))
+ assert_equal(entries1, h1.to_a)
+
+ h1 = {}
+ assert_same(h1, h1.replace(entries2.hash_for))
+ assert_equal(entries2, h1.to_a)
+
+ h1 = entries1.hash_for
+ assert_same(h1, h1.replace({}))
+ assert_predicate(h1, :empty?)
+
+ pairs2 = entries2.dup
+ h2 = pairs2.hash_for
+ pairs2.shift
+ h2.shift
+ h2.delete(pairs2.delete_at(2)[0])
+ h2.delete(pairs2.delete_at(4)[0])
+ h1 = entries1.hash_for
+ assert_same(h1, h1.replace(h2))
+ assert_equal(pairs2, h1.to_a)
+
+ h1 = entries1.hash_for(Hash.new(10))
+ h2 = entries2.hash_for(Hash.new(20))
+ assert_same(h1, h1.replace(h2))
+ assert_equal(entries2, h1.to_a)
+ assert_equal(20, h1.default)
+
+ h1 = entries1.hash_for(Hash.new{_2})
+ h2 = entries2.hash_for(Hash.new{_2.to_s})
+ assert_same(h1, h1.replace(h2))
+ assert_equal(entries2, h1.to_a)
+ assert_equal("-11", h1[-11])
+
+ h1 = entries1.hash_for(Hash.new(10))
+ h2 = entries2.hash_for(Hash.new{_2.to_s})
+ assert_same(h1, h1.replace(h2))
+ assert_equal(entries2, h1.to_a)
+ assert_equal("-11", h1[-11])
+
+ h1 = entries1.hash_for(Hash.new{_2})
+ h2 = entries2.hash_for(Hash.new(20))
+ assert_same(h1, h1.replace(h2))
+ assert_equal(entries2, h1.to_a)
+ assert_equal(20, h1[-1])
+
+ h1 = entries1.hash_for(cls.new(10)){|h| h.foo = 41}
+ h2 = entries2.hash_for(cls.new(20)){|h| h.foo = 42}.freeze
+ assert_same(h1, h1.replace(h2))
+ assert_equal(entries2, h1.to_a)
+ assert_equal(20, h1.default)
+ assert_equal(41, h1.foo)
+
+ h1 = entries1.hash_for
+ h2 = entries2.hash_for(cls.new)
+ assert_same(h1, h1.replace(h2))
+ assert_equal(entries2, h1.to_a)
+
+ assert_raise(TypeError){entries1.hash_for.replace([])}
+
+ k2 = HashKey[-2]
+ pairs2 = entries2.dup
+ pairs2 << [k2, 23]
+ h1 = entries1.hash_for
+ h2 = pairs2.hash_for
+ k2.callback = ->(*){h1.clear; h2.clear}
+ assert_nothing_crashed{h1.replace(h2)}
+
+ assert_raise(FrozenError){h1.freeze.replace(h1)}
+ assert_raise(FrozenError){{}.freeze.replace({})}
+ end
end
-assert('Hash#values', '15.2.13.4.28') do
- a = { 'abc_key' => 'abc_value' }
+assert('Hash#shift', '15.2.13.4.24') do
+ [ar_entries, ht_entries].each do |entries|
+ pairs = entries.dup
+ h = pairs.hash_for
+ h.delete(pairs.delete_at(0)[0])
+ h.delete(pairs.delete_at(3)[0])
+ until pairs.empty?
+ exp = pairs.shift
+ act = h.shift
+ assert_equal(Array, act.class)
+ assert_equal(exp, act)
+ assert_equal(exp.size, act.size)
+ assert_not_operator(h, :key?, exp[0])
+ end
+
+ assert_equal(nil, h.shift)
+ assert_equal(0, h.size)
+
+ h.default = -456
+ assert_equal(nil, h.shift)
+ assert_equal(0, h.size)
+
+ h.freeze
+ assert_raise(FrozenError){h.shift}
+ end
- assert_equal ['abc_value'], a.values
+ h = Hash.new{|h, k| [h, k]}
+ assert_equal(0, h.size)
+ assert_equal(nil, h.shift)
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
+%i[reject select].each do |meth|
+ assert("Hash##{meth}") do
+ cls = Class.new(Hash){attr_accessor :foo}
+ [ar_entries, ht_entries].each do |entries|
+ params = nil
+ filter = ->((k, v)) do
+ params << [k, v]
+ String === k
+ end
+
+ h = entries.hash_for(cls.new(1))
+ params = []
+ ret = h.__send__(meth, &filter)
+ assert_equal(entries, params)
+ assert_equal(entries, h.to_a)
+ assert_equal(1, h.default)
+ assert_equal(entries.__send__(meth, &filter), ret.to_a)
+ assert_equal(Hash, ret.class)
+ assert_equal(nil, ret.default)
+
+ params = []
+ assert_predicate({}.__send__(meth, &filter), :empty?)
+ assert_predicate(params, :empty?)
+ end
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
+%i[reject! select!].each do |meth|
+ assert("Hash##{meth}") do
+ [ar_entries, ht_entries].each do |entries|
+ params = nil
+ filter = ->((k, v)) do
+ params << [k, v]
+ String === k
+ end
+
+ pairs = entries.dup << ["_str", 5]
+ h = pairs.hash_for(Hash.new(1))
+ params = []
+ ret = h.__send__(meth, &filter)
+ assert_same(h, ret)
+ assert_equal(pairs, params)
+ assert_equal(pairs.__send__(meth.to_s[0..-2], &filter), h.to_a)
+ assert_equal(1, h.default)
+
+ h = pairs.hash_for
+ ret = h.__send__(meth){meth == :select!}
+ assert_nil(ret)
+ assert_equal(pairs, h.to_a)
+
+ assert_raise(FrozenError){h.freeze.__send__(meth, &filter)}
+ end
+
+ h = {}
+ assert_nil(h.__send__(meth){})
+ assert_predicate(h, :empty?)
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
+%i[inspect to_s].each do |meth|
+ assert("Hash##{meth}") do
+ assert_equal('{}', Hash.new.__send__(meth))
+
+ h1 = {:s => 0, :a => [1,2], 37 => :b, :d => "del", "c" => nil}
+ h1.shift
+ h1.delete(:d)
+ s1 = ':a=>[1, 2], 37=>:b, "c"=>nil'
+ h2 = Hash.new(100)
+
+ (1..14).each{h2[_1] = _1 * 2}
+ h2 = {**h2, **h1}
+ s2 = "1=>2, 2=>4, 3=>6, 4=>8, 5=>10, 6=>12, 7=>14, 8=>16, " \
+ "9=>18, 10=>20, 11=>22, 12=>24, 13=>26, 14=>28, #{s1}"
+
+ [[h1, s1], [h2, s2]].each do |h, s|
+ assert_equal("{#{s}}", h.__send__(meth))
+
+ hh = {}
+ hh[:recur] = hh
+ h.each{|k, v| hh[k] = v}
+ assert_equal("{:recur=>{...}, #{s}}", hh.__send__(meth))
+
+ hh = h.dup
+ hh[hh] = :recur
+ assert_equal("{#{s}, {...}=>:recur}", hh.__send__(meth))
+ end
+
+ [ar_entries, ht_entries].each do |entries|
+ cls = Class.new do
+ attr_accessor :h
+ def inspect; @h.replace(@h.dup); to_s; end
+ end
+ v = cls.new
+ h = entries.hash_for({_k: v})
+ v.h = h
+ assert_nothing_raised{h.__send__(meth)}
+ end
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
+assert('Hash#rehash') do
+ cls = Class.new(Hash){attr_accessor :foo}
+ [ar_entries, ht_entries].each do |entries|
+ k1, k2, k3 = HashKey[-1], HashKey[-2], HashKey[-3]
+ pairs = entries.dup.push(
+ [-4, -40],
+ [HashKey[-11], -5],
+ [:_del, "_del"],
+ [k1, :_k1],
+ ["_a", "_b"],
+ [k2, :_k2],
+ ["_c", "_d"],
+ [HashKey[-22], -21],
+ [k3, :_k3],
+ )
+ h = pairs.hash_for(cls.new(:defvar)){|h| h.foo = "f"}
+ k1.value, k2.value, k3.value = -11, -11, -22
+ pairs1 = pairs.dup
+ pairs1.delete([:_del, h.delete(:_del)])
+ exp_pairs1 = pairs1.hash_for.to_a
+ h.freeze
+ assert_same(h, h.rehash)
+ assert_equal(exp_pairs1, h.to_a)
+ assert_equal(exp_pairs1.size, h.size)
+ assert_equal(:defvar, h.default)
+ assert_equal("f", h.foo)
+ exp_pairs1.each {|k, v| assert_equal(v, h[k])}
+
+ # If an error occurs during rehash, at least the entry list is not broken.
+ k1.value, k2.value, k3.value = -1, -2, -3
+ h = pairs.hash_for
+ k1.value = -11
+ pairs2 = pairs.dup
+ pairs2.delete([:_del, h.delete(:_del)])
+ exp_pairs2 = pairs2.hash_for.to_a
+ k2.error = :eql?
+ assert_raise{h.rehash}
+ act_pairs2 = h.to_a
+ unless pairs2 == act_pairs2 && pairs2.size == h.size
+ assert_equal(exp_pairs2, act_pairs2)
+ assert_equal(exp_pairs2.size, h.size)
+ end
+
+ k1.value = -1
+ k2.error = false
+ h = pairs.hash_for
+ k1.callback = ->(name, *){h.clear if name == :eql?}
+ assert_nothing_crashed{h.rehash}
+ k1.callback = ->(name, *){h.clear if name == :hash}
+ assert_nothing_crashed{h.rehash}
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'
+ h = {}
+ assert_same(h, h.rehash)
+ assert_predicate(h, :empty?)
+
+ h = {}
+ (1..17).each{h[_1] = _1 * 2}
+ (2..16).each{h.delete(_1)}
+ assert_same(h, h.rehash)
+ assert_equal([[1, 2], [17, 34]], h.to_a)
+ assert_equal(2, h.size)
+ [1, 17].each{assert_equal(_1 * 2, h[_1])}
+end
+
+assert('#eql? receiver should be specified key') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for
+ k0 = HashKey[-99]
+ h[k0] = 1
+
+ k1 = HashKey[-3, error: :eql?]
+ assert_raise{h[k1]}
+ k0.error = :eql?
+ k1.error = false
+ assert_nothing_raised{h[k1]}
+
+ k0.error = false
+ k1.error = :eql?
+ assert_raise{h[k1] = 1}
+ k0.error = :eql?
+ k1.error = false
+ assert_nothing_raised{h[k1] = 1}
+
+ k0.error = false
+ k2 = HashKey[-6, error: :eql?]
+ assert_raise{h.delete(k2)}
+ k0.error = :eql?
+ k2.error = false
+ assert_nothing_raised{h.delete(k2)}
+
+ k0.error = false
+ k3 = HashKey[-9, error: :eql?]
+ %i[has_key? include? key? member?].each do |m|
+ assert_raise{h.__send__(m, k3)}
+ end
+ k0.error = :eql?
+ k3.error = false
+ %i[has_key? include? key? member?].each do |m|
+ assert_nothing_raised{h.__send__(m, k3)}
+ end
+ end
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('#== receiver should be specified value') do
+ [ar_entries, ht_entries].each do |entries|
+ h = entries.hash_for
+ v0 = HashKey[-99]
+ h[-99] = v0
-assert('Hash#freeze') do
- h = {}.freeze
- assert_raise(RuntimeError) do
- h[:a] = 'b'
+ v1 = HashKey[-3, error: :==]
+ %i[has_value? value?].each{|m| assert_raise{h.__send__(m, v1)}}
+ v0.error = :==
+ v1.error = false
+ %i[has_value? value?].each{|m| assert_nothing_raised{h.__send__(m, v1)}}
end
end
diff --git a/test/t/integer.rb b/test/t/integer.rb
index cea97a1e6..d4076f38a 100644
--- a/test/t/integer.rb
+++ b/test/t/integer.rb
@@ -7,97 +7,72 @@ end
assert('Integer#+', '15.2.8.3.1') do
a = 1+1
- b = 1+1.0 if class_defined?("Float")
+ b = 1+1.0 if Object.const_defined?(:Float)
assert_equal 2, a
- assert_equal 2.0, b if class_defined?("Float")
+ assert_equal 2.0, b if Object.const_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")
+ b = 2-1.0 if Object.const_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
+ assert_equal 1.0, b if Object.const_defined?(:Float)
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")
-
+ if Object.const_defined?(:Float)
+ b = 1*1.0
+ assert_equal 1.0, b
+ end
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
+ a = 5/2
+ assert_equal 2, a
+ skip unless Object.const_defined?(:Float)
+ b = 2/1.0
assert_equal 2.0, b
end
+assert('Integer#quo') do
+ a = 6.quo(5)
+ assert_equal 1.2, a
+end if Object.const_defined?(:Float)
+
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
+ b = 2%4
+ c = 2%5
+ d = 2%-5
+ e = -2%5
+ f = -2%-5
+ g = 2%-2
+ h = -2%2
+ i = -2%-2
assert_equal 0, a
- assert_equal 0.0, b
+ assert_equal 2, b
assert_equal 2, c
- assert_equal 2, d
- assert_equal(-3, e)
- assert_equal 3, f
- assert_equal(-2, g)
+ assert_equal(-3, d)
+ assert_equal 3, e
+ assert_equal(-2, f)
+ assert_equal 0, g
assert_equal 0, h
assert_equal 0, i
- assert_equal 0, j
+ skip unless Object.const_defined?(:Float)
+ j = 1%1.0
+ assert_equal 0.0, j
end
assert('Integer#<=>', '15.2.9.3.6') do
@@ -157,11 +132,7 @@ assert('Integer#<<', '15.2.8.3.12') do
# 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)
+ skip unless Object.const_defined?(:Float)
end
assert('Integer#>>', '15.2.8.3.13') do
@@ -226,16 +197,25 @@ assert('Integer#times', '15.2.8.3.22') do
end
assert('Integer#to_f', '15.2.8.3.23') do
+ skip unless Object.const_defined?(:Float)
assert_equal 1.0, 1.to_f
-end if class_defined?("Float")
+end
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)
+ assert_equal "1", 1.to_s
+ assert_equal "-1", -1.to_s
+ assert_equal "1010", 10.to_s(2)
+ assert_equal "a", 10.to_s(36)
+ assert_equal "-a", -10.to_s(36)
+ assert_equal "30071", 12345.to_s(8)
+ assert_raise(ArgumentError) { 10.to_s(-1) }
+ assert_raise(ArgumentError) { 10.to_s(0) }
+ assert_raise(ArgumentError) { 10.to_s(1) }
+ assert_raise(ArgumentError) { 10.to_s(37) }
end
assert('Integer#truncate', '15.2.8.3.26') do
@@ -259,19 +239,3 @@ assert('Integer#divmod', '15.2.8.3.30') do
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
diff --git a/test/t/iterations.rb b/test/t/iterations.rb
index f227a6037..9611388ea 100644
--- a/test/t/iterations.rb
+++ b/test/t/iterations.rb
@@ -58,4 +58,4 @@ assert('next expression', '11.5.2.4.4') do
end
all
end
-end \ No newline at end of file
+end
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index 74176fbd0..0a70ec0d2 100644
--- a/test/t/kernel.rb
+++ b/test/t/kernel.rb
@@ -29,11 +29,7 @@ assert('Kernel.block_given?', '15.3.1.2.2') do
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
+# Kernel.eval is provided by the mruby-eval mrbgem. '15.3.1.2.3'
assert('Kernel.iterator?', '15.3.1.2.5') do
assert_false Kernel.iterator?
@@ -63,20 +59,11 @@ assert('Kernel.loop', '15.3.1.2.8') do
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
+# Kernel.p is provided by the mruby-print mrbgem. '15.3.1.2.9'
-assert('Kernel.print', '15.3.1.2.10') do
- # TODO search for a way to test print to stdio
- assert_true true
-end
+# Kernel.print is provided by the mruby-print mrbgem. '15.3.1.2.10'
-assert('Kernel.puts', '15.3.1.2.11') do
- # TODO search for a way to test puts to stdio
- assert_true true
-end
+# Kernel.puts is provided by the mruby-print mrbgem. '15.3.1.2.11'
assert('Kernel.raise', '15.3.1.2.12') do
assert_raise RuntimeError do
@@ -89,7 +76,25 @@ assert('Kernel.raise', '15.3.1.2.12') do
end
assert('Kernel#__id__', '15.3.1.3.3') do
- assert_equal Fixnum, __id__.class
+ assert_equal Integer, __id__.class
+end
+
+assert('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
+
+ args = [:respond_to?, :nil?]
+ assert_true __send__(*args)
+ assert_equal [:respond_to?, :nil?], args
end
assert('Kernel#block_given?', '15.3.1.3.6') do
@@ -111,6 +116,13 @@ assert('Kernel#block_given?', '15.3.1.3.6') do
"block"
end
end
+
+ def bg_try_in_block
+ -> { block_given? }[]
+ end
+
+ assert_false bg_try_in_block
+ assert_true bg_try_in_block{}
end
assert('Kernel#class', '15.3.1.3.7') do
@@ -187,17 +199,6 @@ assert('Kernel#dup', '15.3.1.3.9') do
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
@@ -230,6 +231,9 @@ assert('Kernel#extend', '15.3.1.3.13') do
assert_true a.respond_to?(:test_method)
assert_false b.respond_to?(:test_method)
+
+ assert_raise(FrozenError) { Object.new.freeze.extend(Test4ExtendModule) }
+ assert_raise(FrozenError, TypeError) { :sym.extend(Test4ExtendModule) }
end
assert('Kernel#extend works on toplevel', '15.3.1.3.13') do
@@ -247,10 +251,23 @@ assert('Kernel#freeze') do
assert_equal obj, obj.freeze
assert_equal 0, 0.freeze
assert_equal :a, :a.freeze
+ assert_equal true, true.freeze
+ assert_equal false, false.freeze
+ assert_equal nil, nil.freeze
+ skip unless Object.const_defined?(:Float)
+ assert_equal 0.0, 0.0.freeze
end
-assert('Kernel#global_variables', '15.3.1.3.14') do
- assert_equal Array, global_variables.class
+assert('Kernel#frozen?') do
+ assert_false "".frozen?
+ assert_true "".freeze.frozen?
+ assert_true 0.frozen?
+ assert_true :a.frozen?
+ assert_true true.frozen?
+ assert_true false.frozen?
+ assert_true nil.frozen?
+ skip unless Object.const_defined?(:Float)
+ assert_true 0.0.frozen?
end
assert('Kernel#hash', '15.3.1.3.15') do
@@ -329,17 +346,15 @@ assert('Kernel#method_missing', '15.3.1.3.30') do
end
end
no_super_test = NoSuperMethodTestClass.new
- begin
+ msg = "no superclass method 'no_super_method_named_this'"
+ assert_raise_with_message(NoMethodError, msg) do
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
+ msg = "undefined method 'no_method_named_this'"
+ assert_raise_with_message(NoMethodError, msg) do
a.no_method_named_this
- rescue NoMethodError => e
- assert_equal "undefined method 'no_method_named_this'", e.message
end
end
@@ -391,11 +406,12 @@ assert('Kernel#remove_instance_variable', '15.3.1.3.41') do
tri = Test4RemoveInstanceVar.new
assert_equal 99, tri.var
- tri.remove
+ assert_equal 99, tri.remove
assert_equal nil, tri.var
- assert_raise NameError do
- tri.remove
- end
+ assert_raise(NameError) { tri.remove }
+ assert_raise(NameError) { tri.remove_instance_variable(:var) }
+ assert_raise(FrozenError) { tri.freeze.remove }
+ assert_raise(FrozenError, NameError) { :a.remove_instance_variable(:@v) }
end
# Kernel#require is defined in mruby-require. '15.3.1.3.42'
@@ -472,13 +488,6 @@ assert('Kernel#respond_to_missing?') do
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
diff --git a/test/t/literals.rb b/test/t/literals.rb
index 6344219aa..f93968650 100644
--- a/test/t/literals.rb
+++ b/test/t/literals.rb
@@ -90,7 +90,7 @@ assert('Literals Strings Here documents', '8.7.6.3.6') do
a = <<AAA
aaa
AAA
- b = <<b_b
+ b = <<b_b
bbb
b_b
c = [<<CCC1, <<"CCC2", <<'CCC3']
@@ -162,6 +162,47 @@ qq
QQ2
"o")
+ r = <<~RRR
+ rrr
+ rrr
+ RRR
+ s = <<~"SSS"
+ sss
+ sss
+ SSS
+ t = <<~'TTT'
+ ttt
+ ttt
+ TTT
+ u = [<<~UUU1 , <<~"UUU2" , <<~'UUU3' ]
+ u#{1}u
+ UUU1
+ u#{2}u
+ UUU2
+ u#{3}u
+ UUU3
+ v1 = <<~VVV
+
+ vvv
+ #{"vvv"}
+ VVV
+ v2 = <<~VVV
+\tvvv
+ vvv
+ VVV
+ v3 = <<~VVV
+ v v v
+ vvv
+ VVV
+ v4 = <<~VVV
+ vvv \
+ vvv
+ VVV
+ v5 = <<~VVV
+ vvv \
+ vvv
+ VVV
+
w = %W( 1 #{<<WWW} 3
www
WWW
@@ -197,10 +238,18 @@ ZZZ
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 "rrr\n rrr\n", r
+ assert_equal "sss\n sss\n", s
+ assert_equal "ttt\n ttt\n", t
+ assert_equal ["u1u\n", "u2u\n", "u\#{3}u\n"], u
+ assert_equal "\nvvv\nvvv\n", v1
+ assert_equal "\tvvv\n vvv\n", v2
+ assert_equal "v v v\n vvv\n", v3
+ assert_equal "vvv vvv\n", v4
+ assert_equal " vvv vvv\n", v5
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
diff --git a/test/t/methods.rb b/test/t/methods.rb
index f9c25dc33..9005d7976 100644
--- a/test/t/methods.rb
+++ b/test/t/methods.rb
@@ -107,3 +107,32 @@ assert('The undef statement (method undefined)', '13.3.7 a) 5)') do
undef :non_existing_method
end
end
+
+assert('method_added hook') do
+ c = Class.new do
+ # method to retrieve @name
+ def self.name; @name; end
+ # hook method on method definition
+ def self.method_added(name) @name = name; end
+ # method definition
+ def foo; end
+ end
+ assert_equal(:foo, c.name)
+ c.define_method(:bar){}
+ assert_equal(:bar, c.name)
+end
+
+assert('singleton_method_added hook') do
+ a = Object.new
+ # method to retrieve @name
+ def a.name; @name; end
+ # hook method on singleton method definition
+ def a.singleton_method_added(name) @name = name; end
+ # singleton method definition
+ def a.foo; end
+ assert_equal(:foo, a.name)
+ class <<a
+ def bar; end
+ end
+ assert_equal(:bar, a.name)
+end
diff --git a/test/t/module.rb b/test/t/module.rb
index 78cb5d07f..9aa018280 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -21,10 +21,29 @@ def labeled_class(name, supklass = Object, &block)
end
end
+def assert_uninitialized_const(&block)
+ assert_raise_with_message_pattern(NameError, "uninitialized constant *", &block)
+end
+
+def assert_wrong_const_name(&block)
+ assert_raise_with_message_pattern(NameError, "wrong constant name *", &block)
+end
+
assert('Module', '15.2.2') do
assert_equal Class, Module.class
end
+assert('Module#alias_method', '15.2.2.4.8') do
+ cls = Class.new do
+ def foo
+ "FOO"
+ end
+ end
+
+ assert_same(cls, cls.alias_method(:bar, :foo))
+ assert_equal("FOO", cls.new.bar)
+end
+
# TODO not implemented ATM assert('Module.constants', '15.2.2.3.1') do
assert('Module#ancestors', '15.2.2.4.9') do
@@ -48,6 +67,7 @@ assert('Module#append_features', '15.2.2.4.10') do
end
assert_equal Test4AppendFeatures2, Test4AppendFeatures2.const_get(:Const4AppendFeatures2)
+ assert_raise(FrozenError) { Module.new.append_features Class.new.freeze }
end
assert('Module#attr NameError') do
@@ -210,6 +230,7 @@ assert('Module#const_defined?', '15.2.2.4.20') do
assert_true Test4ConstDefined.const_defined?(:Const4Test4ConstDefined)
assert_false Test4ConstDefined.const_defined?(:NotExisting)
+ assert_wrong_const_name{ Test4ConstDefined.const_defined?(:wrong_name) }
end
assert('Module#const_get', '15.2.2.4.21') do
@@ -222,8 +243,9 @@ assert('Module#const_get', '15.2.2.4.21') do
assert_equal 42, Object.const_get("Test4ConstGet::Const4Test4ConstGet")
assert_raise(TypeError){ Test4ConstGet.const_get(123) }
- assert_raise(NameError){ Test4ConstGet.const_get(:I_DO_NOT_EXIST) }
- assert_raise(NameError){ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") }
+ assert_uninitialized_const{ Test4ConstGet.const_get(:I_DO_NOT_EXIST) }
+ assert_uninitialized_const{ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") }
+ assert_wrong_const_name{ Test4ConstGet.const_get(:wrong_name) }
end
assert('Module#const_set', '15.2.2.4.23') do
@@ -231,8 +253,11 @@ assert('Module#const_set', '15.2.2.4.23') do
Const4Test4ConstSet = 42
end
- assert_true Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
+ assert_equal 23, Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
assert_equal 23, Test4ConstSet.const_get(:Const4Test4ConstSet)
+ ["", "wrongNAME", "Wrong-Name"].each do |n|
+ assert_wrong_const_name { Test4ConstSet.const_set(n, 1) }
+ end
end
assert('Module#remove_const', '15.2.2.4.40') do
@@ -240,21 +265,15 @@ assert('Module#remove_const', '15.2.2.4.40') do
ExistingConst = 23
end
- result = Test4RemoveConst.module_eval { remove_const :ExistingConst }
-
- name_error = false
- begin
- Test4RemoveConst.module_eval { remove_const :NonExistingConst }
- rescue NameError
- name_error = true
+ assert_equal 23, Test4RemoveConst.remove_const(:ExistingConst)
+ assert_false Test4RemoveConst.const_defined?(:ExistingConst)
+ assert_raise_with_message_pattern(NameError, "constant * not defined") do
+ Test4RemoveConst.remove_const(:NonExistingConst)
end
-
- # Constant removed from Module
- assert_false Test4RemoveConst.const_defined? :ExistingConst
- # Return value of binding
- assert_equal 23, result
- # Name Error raised when Constant doesn't exist
- assert_true name_error
+ %i[x X!].each do |n|
+ assert_wrong_const_name { Test4RemoveConst.remove_const(n) }
+ end
+ assert_raise(FrozenError) { Test4RemoveConst.freeze.remove_const(:A) }
end
assert('Module#const_missing', '15.2.2.4.22') do
@@ -267,6 +286,18 @@ assert('Module#const_missing', '15.2.2.4.22') do
assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist)
end
+assert('Module#extend_object', '15.2.2.4.25') do
+ cls = Class.new
+ mod = Module.new { def foo; end }
+ a = cls.new
+ b = cls.new
+ mod.extend_object(b)
+ assert_false a.respond_to?(:foo)
+ assert_true b.respond_to?(:foo)
+ assert_raise(FrozenError) { mod.extend_object(cls.new.freeze) }
+ assert_raise(FrozenError, TypeError) { mod.extend_object(1) }
+end
+
assert('Module#include', '15.2.2.4.27') do
module Test4Include
Const4Include = 42
@@ -280,6 +311,7 @@ assert('Module#include', '15.2.2.4.27') do
assert_equal 42, Test4Include2.const_get(:Const4Include)
assert_equal Test4Include2, Test4Include2.include_result
+ assert_raise(FrozenError) { Module.new.freeze.include Test4Include }
end
assert('Module#include?', '15.2.2.4.28') do
@@ -359,15 +391,15 @@ assert('Module#undef_method', '15.2.2.4.42') do
class Parent
def hello
end
- end
+ end
- class Child < Parent
+ class Child < Parent
def hello
end
- end
+ end
- class GrandChild < Child
- end
+ class GrandChild < Child
+ end
end
Test4UndefMethod::Child.class_eval{ undef_method :hello }
@@ -378,6 +410,29 @@ end
# Not ISO specified
+assert('Module#dup') do
+ module TestModuleDup
+ @@cvar = :cvar
+ class << self
+ attr_accessor :cattr
+ def cmeth; :cmeth end
+ end
+ def cvar; @@cvar end
+ def imeth; :imeth end
+ self.cattr = :cattr
+ end
+
+ m = TestModuleDup.dup
+ o = Object.include(m).new
+ assert_equal(:cattr, m.cattr)
+ assert_equal(:cmeth, m.cmeth)
+ assert_equal(:cvar, o.cvar)
+ assert_equal(:imeth, o.imeth)
+ assert_match("#<Module:0x*>", m.to_s)
+ assert_not_predicate(m, :frozen?)
+ assert_not_predicate(TestModuleDup.freeze.dup, :frozen?)
+end
+
assert('Module#define_method') do
c = Class.new {
define_method(:m1) { :ok }
@@ -390,6 +445,15 @@ assert('Module#define_method') do
end
end
+assert 'Module#prepend_features' do
+ mod = Module.new { def m; :mod end }
+ cls = Class.new { def m; :cls end }
+ assert_equal :cls, cls.new.m
+ mod.prepend_features(cls)
+ assert_equal :mod, cls.new.m
+ assert_raise(FrozenError) { Module.new.prepend_features(Class.new.freeze) }
+end
+
# @!group prepend
assert('Module#prepend') do
module M0
@@ -529,7 +593,7 @@ end
# to_f / other
# end
# end
- # Fixnum.send(:prepend, M)
+ # Integer.send(:prepend, M)
# assert_equal(0.5, 1 / 2, "#{bug7983}")
# }
# assert_equal(0, 1 / 2)
@@ -610,7 +674,7 @@ end
c1 = labeled_class("C1")
c2 = labeled_class("C2", c1) {prepend m}
c1.class_eval {prepend m}
- assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
+ assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accessible prepended module in superclass")
end
# requires #assert_separately
@@ -624,6 +688,10 @@ end
# end
# end;
#end
+
+ assert 'Module#prepend to frozen class' do
+ assert_raise(FrozenError) { Class.new.freeze.prepend Module.new }
+ end
# @!endgroup prepend
assert('Module#to_s') do
@@ -644,11 +712,12 @@ assert('Module#to_s') do
assert_equal 'SetOuter', SetOuter.to_s
assert_equal 'SetOuter::SetInner', SetOuter::SetInner.to_s
- mod = Module.new
- cls = Class.new
+ assert_match "#<Module:0x*>", Module.new.to_s
+ assert_match "#<Class:0x*>", Class.new.to_s
- assert_equal "#<Module:0x", mod.to_s[0,11]
- assert_equal "#<Class:0x", cls.to_s[0,10]
+ assert_equal "FrozenClassToS", (FrozenClassToS = Class.new.freeze).to_s
+ assert_equal "Outer::A", (Outer::A = Module.new.freeze).to_s
+ assert_match "#<Module:0x*>::A", (Module.new::A = Class.new.freeze).to_s
end
assert('Module#inspect') do
@@ -667,17 +736,17 @@ assert('Issue 1467') do
class C1
include M1
- def initialize()
- super()
- end
+ def initialize()
+ super()
+ end
end
class C2
include M1
end
- C1.new
- C2.new
+ assert_kind_of(M1, C1.new)
+ assert_kind_of(M1, C2.new)
end
assert('clone Module') do
@@ -691,7 +760,7 @@ assert('clone Module') do
include M1.clone
end
- B.new.foo
+ assert_true(B.new.foo)
end
assert('Module#module_function') do
@@ -708,6 +777,15 @@ assert('module with non-class/module outer raises TypeError') do
assert_raise(TypeError) { module []::M2 end }
end
+assert('module to return the last value') do
+ m = module M; :m end
+ assert_equal(m, :m)
+end
+
+assert('module to return nil if body is empty') do
+ assert_nil(module M end)
+end
+
assert('get constant of parent module in singleton class; issue #3568') do
actual = module GetConstantInSingletonTest
EXPECTED = "value"
diff --git a/test/t/nameerror.rb b/test/t/nameerror.rb
index 28682bedc..9d2e2a470 100644
--- a/test/t/nameerror.rb
+++ b/test/t/nameerror.rb
@@ -20,9 +20,9 @@ assert('NameError#name', '15.2.31.2.1') do
end
assert('NameError#initialize', '15.2.31.2.2') do
- e = NameError.new('a', :foo)
+ e = NameError.new('a', :foo)
- assert_equal NameError, e.class
- assert_equal 'a', e.message
- assert_equal :foo, e.name
+ assert_equal NameError, e.class
+ assert_equal 'a', e.message
+ assert_equal :foo, e.name
end
diff --git a/test/t/numeric.rb b/test/t/numeric.rb
index 38c62a669..8baf6c883 100644
--- a/test/t/numeric.rb
+++ b/test/t/numeric.rb
@@ -1,8 +1,21 @@
##
# Numeric ISO Test
+def assert_step(exp, receiver, args, inf: false)
+ act = []
+ ret = receiver.step(*args) do |i|
+ act << i
+ break if inf && exp.size == act.size
+ end
+ expr = "#{receiver.inspect}.step(#{args.map(&:inspect).join(', ')})"
+ assert "assert_step" do
+ assert_true(exp.eql?(act), "#{expr}: counters", assertion_diff(exp, act))
+ assert_same(receiver, ret, "#{expr}: return value") unless inf
+ end
+end
+
assert('Numeric', '15.2.7') do
- assert_equal Class, Numeric.class
+ assert_equal(Class, Numeric.class)
end
assert('Numeric#+@', '15.2.7.4.1') do
@@ -15,22 +28,15 @@ 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)
+ skip unless Object.const_defined?(:Float)
+ assert_equal(1.0, -1.abs)
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(0, 1/16)
assert_equal(15.1, n/10)
assert_raise(TypeError){ 1/n }
assert_raise(TypeError){ 1/nil }
@@ -39,5 +45,70 @@ end
# Not ISO specified
assert('Numeric#**') do
- assert_equal 8.0, 2.0**3
+ assert_equal(8, 2 ** 3)
+ assert_equal(-8, -2 ** 3)
+ assert_equal(1, 2 ** 0)
+ skip unless Object.const_defined?(:Float)
+ assert_equal(1.0, 2.2 ** 0)
+ assert_equal(0.5, 2 ** -1)
+ assert_equal(8.0, 2.0**3)
+end
+
+assert('Numeric#step') do
+ assert_raise(ArgumentError) { 1.step(2, 0) { break } }
+ assert_step([2, 3, 4], 2, [4])
+ assert_step([10, 8, 6, 4, 2], 10, [1, -2])
+ assert_step([], 2, [1, 3])
+ assert_step([], -2, [-1, -3])
+ assert_step([10, 11, 12, 13], 10, [], inf: true)
+ assert_step([10, 7, 4], 10, [nil, -3], inf: true)
+
+ skip unless Object.const_defined?(:Float)
+ inf = Float::INFINITY
+ assert_raise(ArgumentError) { 1.step(2, 0.0) { break } }
+ assert_step([2.0, 3.0, 4.0], 2, [4.0])
+ assert_step([7.0, 4.0, 1.0, -2.0], 7, [-4, -3.0])
+ assert_step([2.0, 3.0, 4.0], 2.0, [4])
+ assert_step([10.0, 11.0, 12.0, 13.0], 10.0, [], inf: true)
+ assert_step([10.0, 7.0, 4.0], 10, [nil, -3.0], inf: true)
+ assert_step([1.0], 1, [nil, inf])
+ assert_step([1.0], 1, [nil, -inf])
+ assert_step([1.0], 1, [3, inf])
+ assert_step([], 1, [-3, inf])
+ assert_step([], 1, [3, -inf])
+ assert_step([1.0], 1, [-3, -inf])
+ assert_step([1.0], 1, [inf, inf])
+ assert_step([], 1, [inf, -inf])
+ assert_step([], 1, [-inf, inf])
+ assert_step([1.0], 1, [-inf, -inf])
+ assert_step([], inf, [2])
+ assert_step([], inf, [-2])
+ assert_step([], inf, [2, 3])
+ assert_step([inf, inf, inf], inf, [2, -3], inf: true)
+ assert_step([], inf, [2, inf])
+ assert_step([inf], inf, [2, -inf])
+ assert_step([], inf, [-2, inf])
+ assert_step([inf], inf, [-2, -inf])
+ assert_step([], inf, [-2, 3])
+ assert_step([inf, inf, inf], inf, [-2, -3], inf: true)
+ assert_step([inf], inf, [inf])
+ assert_step([], inf, [-inf])
+ assert_step([inf], inf, [inf, inf])
+ assert_step([inf], inf, [inf, -inf])
+ assert_step([inf], inf, [-inf, -inf])
+ assert_step([-inf, -inf, -inf], -inf, [2], inf: true)
+ assert_step([-inf, -inf, -inf], -inf, [-2], inf: true)
+ assert_step([-inf, -inf, -inf], -inf, [2, 3], inf: true)
+ assert_step([], -inf, [2, -3])
+ assert_step([-inf], -inf, [2, inf])
+ assert_step([], -inf, [2, -inf])
+ assert_step([-inf], -inf, [-2, inf])
+ assert_step([], -inf, [-2, -inf])
+ assert_step([-inf, -inf, -inf], -inf, [-2, 3], inf: true)
+ assert_step([], -inf, [-2, -3])
+ assert_step([-inf, -inf, -inf], -inf, [inf], inf: true)
+ assert_step([-inf], -inf, [-inf])
+ assert_step([-inf], -inf, [inf, inf])
+ assert_step([], -inf, [inf, -inf])
+ assert_step([-inf], -inf, [-inf, -inf])
end
diff --git a/test/t/object.rb b/test/t/object.rb
index 6a755d3ba..b65f298e6 100644
--- a/test/t/object.rb
+++ b/test/t/object.rb
@@ -8,4 +8,3 @@ end
assert('Object superclass', '15.2.1.2') do
assert_equal BasicObject, Object.superclass
end
-
diff --git a/test/t/proc.rb b/test/t/proc.rb
index 42ac3b941..b17b21e8c 100644
--- a/test/t/proc.rb
+++ b/test/t/proc.rb
@@ -157,7 +157,7 @@ assert('&obj call to_proc if defined') do
def mock(&b)
b
end
- assert_equal pr.object_id, mock(&pr).object_id
+ assert_same pr, mock(&pr)
assert_equal pr, mock(&pr)
obj = Object.new
diff --git a/test/t/range.rb b/test/t/range.rb
index 3e67fcc1c..5f29b4514 100644
--- a/test/t/range.rb
+++ b/test/t/range.rb
@@ -8,18 +8,39 @@ 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")
+ assert_false (1..10) == (1..)
+ assert_false (1..10) == (..10)
+
+ assert_true (1..) == (1..nil)
+ assert_true (1..) == (1..)
+ assert_false (1..) == (1...)
+
+ assert_true (..1) == (nil..1)
+ assert_true (..1) == (..1)
+ assert_false (..1) == (...1)
+
+ skip unless Object.const_defined?(:Float)
+ assert_true (1..10) == Range.new(1.0, 10.0)
+
end
assert('Range#===', '15.2.14.4.2') do
a = (1..10)
+ b = (1..)
+ c = (..10)
assert_true a === 5
assert_false a === 20
+ assert_true b === 20
+ assert_false b === 0
+ assert_false c === 20
+ assert_true c === 0
end
assert('Range#begin', '15.2.14.4.3') do
assert_equal 1, (1..10).begin
+ assert_equal 1, (1..).begin
+ assert_nil (..1).begin
end
assert('Range#each', '15.2.14.4.4') do
@@ -27,27 +48,45 @@ assert('Range#each', '15.2.14.4.4') do
b = 0
a.each {|i| b += i}
assert_equal 6, b
+ c = []
+ (1..).each { |i| c << i; break if c.size == 10 }
+ assert_equal [1, 2, 3, 4, 5, 6, 7, 8 ,9, 10], c
end
assert('Range#end', '15.2.14.4.5') do
assert_equal 10, (1..10).end
+ assert_nil (1..).end
+ assert_equal 10, (..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?
+ assert_true (1...).exclude_end?
+ assert_false (1..).exclude_end?
+ assert_true (...1).exclude_end?
+ assert_false (..1).exclude_end?
end
assert('Range#first', '15.2.14.4.7') do
assert_equal 1, (1..10).first
+ assert_equal 1, (1..).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..).include?(10)
+ assert_false (1..).include?(0)
+ assert_true (..10).include?(10)
+ assert_true (..10).include?(0)
assert_true (1...10).include?(9)
assert_false (1...10).include?(10)
+ assert_true (1...).include?(10)
+ assert_false (1...).include?(0)
+ assert_false (...10).include?(10)
+ assert_true (...10).include?(0)
end
assert('Range#initialize', '15.2.14.4.9') do
@@ -59,18 +98,30 @@ assert('Range#initialize', '15.2.14.4.9') do
assert_equal (1..10), b
assert_false b.exclude_end?
- assert_raise(NameError) { (0..1).send(:initialize, 1, 3) }
+ assert_raise(NameError) { (0..1).__send__(:initialize, 1, 3) }
+
+ c = Range.new(1, nil, true)
+ d = Range.new(1, nil, false)
+
+ assert_equal (1...nil), c
+ assert_true c.exclude_end?
+ assert_equal (1..nil), d
+ assert_false d.exclude_end?
end
assert('Range#last', '15.2.14.4.10') do
assert_equal 10, (1..10).last
+ assert_nil (1..).last
end
assert('Range#member?', '15.2.14.4.11') do
a = (1..10)
+ b = (1..)
assert_true a.member?(5)
assert_false a.member?(20)
+ assert_true b.member?(20)
+ assert_false b.member?(0)
end
assert('Range#to_s', '15.2.14.4.12') do
@@ -78,6 +129,10 @@ assert('Range#to_s', '15.2.14.4.12') do
assert_equal "0...1", (0...1).to_s
assert_equal "a..b", ("a".."b").to_s
assert_equal "a...b", ("a"..."b").to_s
+ assert_equal "0..", (0..).to_s
+ assert_equal "0...", (0...).to_s
+ assert_equal "a..", ("a"..).to_s
+ assert_equal "a...", ("a"...).to_s
end
assert('Range#inspect', '15.2.14.4.13') do
@@ -85,11 +140,55 @@ assert('Range#inspect', '15.2.14.4.13') do
assert_equal "0...1", (0...1).inspect
assert_equal "\"a\"..\"b\"", ("a".."b").inspect
assert_equal "\"a\"...\"b\"", ("a"..."b").inspect
+ assert_equal "0..", (0..).inspect
+ assert_equal "0...", (0...).inspect
+ assert_equal "\"a\"..", ("a"..).inspect
+ assert_equal "\"a\"...", ("a"...).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"
+ assert_true (1..).eql? (1..)
+ assert_false (1..).eql? (2..)
+ assert_false (1..).eql? "1.."
+ skip unless Object.const_defined?(:Float)
+ assert_false (1..10).eql? (Range.new(1.0, 10.0))
+ assert_false (1..).eql? (Range.new(1.0, nil))
+end
+
+assert('Range#initialize_copy', '15.2.14.4.15') do
+ assert_raise(NameError) { (0..1).__send__(:initialize_copy, 1..3) }
+end
+
+assert('Range#hash', '15.3.1.3.15') do
+ assert_kind_of(Integer, (1..10).hash)
+ assert_equal (1..10).hash, (1..10).hash
+ assert_not_equal (1..10).hash, (1...10).hash
+ assert_equal (1..).hash, (1..).hash
+ assert_not_equal (1..).hash, (1...).hash
+end
+
+assert('Range#dup') do
+ r = (1..3).dup
+ assert_equal 1, r.begin
+ assert_equal 3, r.end
+ assert_false r.exclude_end?
+
+ r = ("a"..."z").dup
+ assert_equal "a", r.begin
+ assert_equal "z", r.end
+ assert_true r.exclude_end?
+
+ r = (1..).dup
+ assert_equal 1, r.begin
+ assert_nil r.end
+ assert_false r.exclude_end?
+end
+
+assert('Range#to_a') do
+ assert_equal([1, 2, 3, 4, 5], (1..5).to_a)
+ assert_equal([1, 2, 3, 4], (1...5).to_a)
+ assert_raise(RangeError) { (1..).to_a }
end
diff --git a/test/t/string.rb b/test/t/string.rb
index e91b915fe..447493227 100644
--- a/test/t/string.rb
+++ b/test/t/string.rb
@@ -2,7 +2,7 @@
##
# String ISO Test
-UTF8STRING = ("\343\201\202".size == 1)
+UTF8STRING = __ENCODING__ == "UTF-8"
assert('String', '15.2.10') do
assert_equal Class, String.class
@@ -37,49 +37,37 @@ 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
+ assert_raise(ArgumentError) { 'a' * -1 }
+ assert_raise(TypeError) { 'a' * '1' }
+ assert_raise(TypeError) { 'a' * nil }
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal 'aa', 'a' * 2.1
+ assert_raise(RangeError) { '' * 1e30 }
+ assert_raise(RangeError) { '' * Float::INFINITY }
+ assert_raise(RangeError) { '' * Float::NAN }
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]
+ assert_equal 'a', 'abc'[0]
+ assert_equal 'c', 'abc'[-1]
+ assert_nil 'abc'[10]
+ assert_nil 'abc'[-10]
+ assert_equal 'b', 'abc'[1.1] if Object.const_defined?(:Float)
# 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.
+ assert_nil 'abc'[0, -1]
+ assert_nil 'abc'[10, 0]
+ assert_nil 'abc'[-10, 0]
+ assert_equal '', 'abc'[0, 0]
+ assert_equal 'bc', 'abc'[1, 2]
# 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
+ assert_equal 'bc', 'abc'['bc']
+ assert_nil 'abc'['XX']
+
+ assert_raise(TypeError) { 'abc'[nil] }
end
assert('String#[](UTF-8)', '15.2.10.5.6') do
@@ -102,6 +90,7 @@ assert('String#[] with Range') do
h1 = 'abc'[3..4]
i1 = 'abc'[4..5]
j1 = 'abcdefghijklmnopqrstuvwxyz'[1..3]
+ k1 = 'abcdefghijklmnopqrstuvwxyz'[-3..]
a2 = 'abc'[1...0]
b2 = 'abc'[1...1]
c2 = 'abc'[1...2]
@@ -112,6 +101,7 @@ assert('String#[] with Range') do
h2 = 'abc'[3...4]
i2 = 'abc'[4...5]
j2 = 'abcdefghijklmnopqrstuvwxyz'[1...3]
+ k2 = 'abcdefghijklmnopqrstuvwxyz'[-3...]
assert_equal '', a1
assert_equal 'b', b1
@@ -123,6 +113,7 @@ assert('String#[] with Range') do
assert_equal '', h1
assert_nil i2
assert_equal 'bcd', j1
+ assert_equal 'xyz', k1
assert_equal '', a2
assert_equal '', b2
assert_equal 'b', c2
@@ -133,6 +124,7 @@ assert('String#[] with Range') do
assert_equal '', h2
assert_nil i2
assert_equal 'bc', j2
+ assert_equal 'xyz', k2
end
assert('String#[]=') do
@@ -155,12 +147,15 @@ assert('String#[]=') do
d[-10] = 'X'
end
- if class_defined?("Float")
- e = 'abc'
- e[1.1] = 'X'
- assert_equal 'aXc', e
+ if Object.const_defined?(:Float)
+ e = 'abc'
+ e[1.1] = 'X'
+ assert_equal 'aXc', e
end
+ assert_raise(TypeError) { 'a'[0] = 1 }
+ assert_raise(TypeError) { 'a'[:a] = '1' }
+
# length of args is 2
a1 = 'abc'
assert_raise(IndexError) do
@@ -197,8 +192,62 @@ assert('String#[]=') do
assert_raise(IndexError) do
b3['XX'] = 'Y'
end
+
+ assert_raise(TypeError) { 'a'[:a, 0] = '1' }
+ assert_raise(TypeError) { 'a'[0, :a] = '1' }
+ assert_raise(TypeError) { 'a'[0, 1] = 1 }
end
+assert('String[]=(UTF-8)') do
+ a = "➀➁➂➃➄"
+ a[3] = "⚃"
+ assert_equal "➀➁➂⚃➄", a
+
+ b = "➀➁➂➃➄"
+ b[3, 0] = "⛄"
+ assert_equal "➀➁➂⛄➃➄", b
+
+ c = "➀➁➂➃➄"
+ c[3, 2] = "⚃⚄"
+ assert_equal "➀➁➂⚃⚄", c
+
+ d = "➀➁➂➃➄"
+ d[5] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", d
+
+ e = "➀➁➂➃➄"
+ e[5, 0] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", e
+
+ f = "➀➁➂➃➄"
+ f[5, 2] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", f
+
+ g = "➀➁➂➃➄"
+ assert_raise(IndexError) { g[6] = "⛄" }
+
+ h = "➀➁➂➃➄"
+ assert_raise(IndexError) { h[6, 0] = "⛄" }
+
+ i = "➀➁➂➃➄"
+ assert_raise(IndexError) { i[6, 2] = "⛄" }
+
+ j = "➀➁➂➃➄"
+ j["➃"] = "⚃"
+ assert_equal "➀➁➂⚃➄", j
+
+ k = "➀➁➂➃➄"
+ assert_raise(IndexError) { k["⛄"] = "⛇" }
+
+ l = "➀➁➂➃➄"
+ assert_nothing_raised { l["➂"] = "" }
+ assert_equal "➀➁➃➄", l
+
+ m = "➀➁➂➃➄"
+ assert_raise(TypeError) { m["➂"] = nil }
+ assert_equal "➀➁➂➃➄", m
+end if UTF8STRING
+
assert('String#capitalize', '15.2.10.5.7') do
a = 'abc'
a.capitalize
@@ -253,19 +302,6 @@ assert('String#chomp!', '15.2.10.5.10') do
assert_equal 'abc', e
end
-assert('String#chomp! uses the correct length') do
- class A
- def to_str
- $s.replace("AA")
- "A"
- end
- end
-
- $s = "AAA"
- $s.chomp!(A.new)
- assert_equal $s, "A"
-end
-
assert('String#chop', '15.2.10.5.11') do
a = ''.chop
b = 'abc'.chop
@@ -419,7 +455,22 @@ assert('String#index', '15.2.10.5.22') do
assert_equal 3, 'abcabc'.index('a', 1)
assert_equal 5, "hello".index("", 5)
assert_equal nil, "hello".index("", 6)
-end
+ assert_equal 3, "hello".index("l", -2)
+ assert_raise(ArgumentError) { "hello".index }
+ assert_raise(TypeError) { "hello".index(101) }
+end
+
+assert('String#index(UTF-8)', '15.2.10.5.22') do
+ assert_equal 0, '⓿➊➋➌➍➎'.index('⓿')
+ assert_nil '⓿➊➋➌➍➎'.index('➓')
+ assert_equal 6, '⓿➊➋➌➍➎⓿➊➋➌➍➎'.index('⓿', 1)
+ assert_equal 6, '⓿➊➋➌➍➎⓿➊➋➌➍➎'.index('⓿', -7)
+ assert_equal 6, "⓿➊➋➌➍➎".index("", 6)
+ assert_equal nil, "⓿➊➋➌➍➎".index("", 7)
+ assert_equal 0, '⓿➊➋➌➍➎'.index("\xe2")
+ assert_equal nil, '⓿➊➋➌➍➎'.index("\xe3")
+ assert_equal 6, "\xd1\xd1\xd1\xd1\xd1\xd1⓿➊➋➌➍➎".index('⓿')
+end if UTF8STRING
assert('String#initialize', '15.2.10.5.23') do
a = ''
@@ -477,12 +528,12 @@ assert('String#reverse', '15.2.10.5.29') do
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 "世", "こんにちは世界"["世"]
+ a = 'こんにちは世界!'
+ a.reverse
+
+ assert_equal 'こんにちは世界!', a
+ assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse
+ assert_equal 'あ', 'あ'.reverse
end if UTF8STRING
assert('String#reverse!', '15.2.10.5.30') do
@@ -499,24 +550,44 @@ assert('String#reverse!(UTF-8)', '15.2.10.5.30') do
assert_equal '!界世はちにんこ', a
assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse!
+
+ b = 'あ'
+ b.reverse!
+ assert_equal 'あ', b
end if UTF8STRING
assert('String#rindex', '15.2.10.5.31') do
assert_equal 0, 'abc'.rindex('a')
+ assert_equal 0, 'abc'.rindex('a', 3)
+ assert_nil 'abc'.rindex('a', -4)
assert_nil 'abc'.rindex('d')
+ assert_equal 6, 'abcabc'.rindex('')
+ assert_equal 3, 'abcabc'.rindex('a')
assert_equal 0, 'abcabc'.rindex('a', 1)
assert_equal 3, 'abcabc'.rindex('a', 4)
+ assert_equal 0, 'abcabc'.rindex('a', -4)
+ assert_raise(ArgumentError) { "hello".rindex }
+ assert_raise(TypeError) { "hello".rindex(101) }
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("さ")
+ assert_nil str.rindex('さ')
+ assert_equal 12, str.rindex('ち')
+ assert_equal 3, str.rindex('ち', 10)
+ assert_equal 3, str.rindex('ち', -6)
+
+ broken = "\xf0☀\xf1☁\xf2☂\xf3☃\xf0☀\xf1☁\xf2☂\xf3☃"
+ assert_nil broken.rindex("\x81") # "\x81" is a part of "☁" ("\xe2\x98\x81")
+ assert_equal 11, broken.rindex("☁")
+ assert_equal 11, broken.rindex("☁", 12)
+ assert_equal 11, broken.rindex("☁", 11)
+ assert_equal 3, broken.rindex("☁", 10)
end if UTF8STRING
-# 'String#scan', '15.2.10.5.32' will be tested in mrbgems.
+# assert('String#scan', '15.2.10.5.32') do
+# # Not implemented yet
+# end
assert('String#size', '15.2.10.5.33') do
assert_equal 3, 'abc'.size
@@ -592,7 +663,7 @@ assert('String#sub', '15.2.10.5.36') do
str = "abc"
miss = str.sub("X", "Z")
assert_equal str, miss
- assert_not_equal str.object_id, miss.object_id
+ assert_not_same str, miss
a = []
assert_equal '.abc', "abc".sub("") { |i| a << i; "." }
@@ -620,31 +691,45 @@ assert('String#sub!', '15.2.10.5.37') do
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_operator(0.0, :eql?, ''.to_f)
+ assert_operator(123456789.0, :eql?, '123456789'.to_f)
+ assert_operator(12345.6789, :eql?, '12345.6789'.to_f)
+ assert_operator(0.0, :eql?, '1e-2147483648'.to_f)
+ assert_operator(Float::INFINITY, :eql?, '1e2147483648'.to_f)
+ assert_operator(0.0, :eql?, 'a'.to_f)
+ assert_operator(4.0, :eql?, '4a5'.to_f)
+ assert_operator(12.0, :eql?, '1_2__3'.to_f)
+ assert_operator(123.0, :eql?, '1_2_3'.to_f)
+ assert_operator(68.0, :eql?, '68_'.to_f)
+ assert_operator(68.0, :eql?, '68._7'.to_f)
+ assert_operator(68.7, :eql?, '68.7_'.to_f)
+ assert_operator(68.7, :eql?, '68.7_ '.to_f)
+ assert_operator(6.0, :eql?, '6 8.7'.to_f)
+ assert_operator(68.0, :eql?, '68. 7'.to_f)
+ assert_operator(0.0, :eql?, '_68'.to_f)
+ assert_operator(0.0, :eql?, ' _68'.to_f)
+ assert_operator(12.34, :eql?, '1_2.3_4'.to_f)
+ assert_operator(12.3, :eql?, '1_2.3__4'.to_f)
+ assert_operator(0.9, :eql?, '.9'.to_f)
+ assert_operator(0.9, :eql?, "\t\r\n\f\v .9 \t\r\n\f\v".to_f)
+end if Object.const_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
+ assert_operator 0, :eql?, ''.to_i
+ assert_operator 32143, :eql?, '32143'.to_i
+ assert_operator 10, :eql?, 'a'.to_i(16)
+ assert_operator 4, :eql?, '100'.to_i(2)
+ assert_operator 1_000, :eql?, '1_000'.to_i
+ assert_operator 0, :eql?, 'a'.to_i
+ assert_operator 4, :eql?, '4a5'.to_i
+ assert_operator 12, :eql?, '1_2__3'.to_i
+ assert_operator 123, :eql?, '1_2_3'.to_i
+ assert_operator 68, :eql?, '68_'.to_i
+ assert_operator 68, :eql?, '68_ '.to_i
+ assert_operator 0, :eql?, '_68'.to_i
+ assert_operator 0, :eql?, ' _68'.to_i
+ assert_operator 68, :eql?, "\t\r\n\f\v 68 \t\r\n\f\v".to_i
+ assert_operator 6, :eql?, ' 6 8 '.to_i
end
assert('String#to_s', '15.2.10.5.40') do
@@ -681,12 +766,18 @@ assert('String#upcase!', '15.2.10.5.43') do
end
assert('String#inspect', '15.2.10.5.46') do
+ assert_equal "\"\\x00\"", "\0".inspect
+ assert_equal "\"foo\"", "foo".inspect
+ if UTF8STRING
+ assert_equal '"る"', "る".inspect
+ else
+ assert_equal '"\xe3\x82\x8b"', "る".inspect
+ end
+
# should not raise an exception - regress #1210
assert_nothing_raised do
- ("\1" * 100).inspect
+ ("\1" * 100).inspect
end
-
- assert_equal "\"\\x00\"", "\0".inspect
end
# Not ISO specified
@@ -696,10 +787,6 @@ assert('String interpolation (mrb_str_concat for shared strings)') do
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]
@@ -725,5 +812,90 @@ assert('String#freeze') do
str = "hello"
str.freeze
- assert_raise(RuntimeError) { str.upcase! }
+ assert_raise(FrozenError) { str.upcase! }
+end
+
+assert('String literal concatenation') do
+ assert_equal 2, ("A" "B").size
+ assert_equal 3, ('A' "B" 'C').size
+ assert_equal 4, (%(A) "B#{?C}" "D").size
+end
+
+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"
+ str2 = "\u3042ab" # "\xE3\x81\x82ab"
+
+ assert_equal("h", str1.byteslice(0))
+ assert_equal("e", str1.byteslice(1))
+ assert_equal(nil, str1.byteslice(5))
+ assert_equal("o", str1.byteslice(-1))
+ assert_equal(nil, str1.byteslice(-6))
+ assert_equal("\xE3", str2.byteslice(0))
+ assert_equal("\x81", str2.byteslice(1))
+ assert_equal(nil, str2.byteslice(5))
+ assert_equal("b", str2.byteslice(-1))
+ assert_equal(nil, str2.byteslice(-6))
+
+ assert_equal("", str1.byteslice(0, 0))
+ assert_equal(str1, str1.byteslice(0, 6))
+ assert_equal("el", str1.byteslice(1, 2))
+ assert_equal("", str1.byteslice(5, 1))
+ assert_equal("o", str1.byteslice(-1, 6))
+ assert_equal(nil, str1.byteslice(-6, 1))
+ assert_equal(nil, str1.byteslice(0, -1))
+ assert_equal("", str2.byteslice(0, 0))
+ assert_equal(str2, str2.byteslice(0, 6))
+ assert_equal("\x81\x82", str2.byteslice(1, 2))
+ assert_equal("", str2.byteslice(5, 1))
+ assert_equal("b", str2.byteslice(-1, 6))
+ assert_equal(nil, str2.byteslice(-6, 1))
+ assert_equal(nil, str2.byteslice(0, -1))
+
+ assert_equal("ell", str1.byteslice(1..3))
+ assert_equal("el", str1.byteslice(1...3))
+ assert_equal("h", str1.byteslice(0..0))
+ assert_equal("", str1.byteslice(5..0))
+ assert_equal("o", str1.byteslice(4..5))
+ assert_equal(nil, str1.byteslice(6..0))
+ assert_equal("", str1.byteslice(-1..0))
+ assert_equal("llo", str1.byteslice(-3..5))
+ assert_equal("\x81\x82a", str2.byteslice(1..3))
+ assert_equal("\x81\x82", str2.byteslice(1...3))
+ assert_equal("\xE3", str2.byteslice(0..0))
+ assert_equal("", str2.byteslice(5..0))
+ assert_equal("b", str2.byteslice(4..5))
+ assert_equal(nil, str2.byteslice(6..0))
+ assert_equal("", str2.byteslice(-1..0))
+ assert_equal("\x82ab", str2.byteslice(-3..5))
+
+ assert_raise(ArgumentError) { str1.byteslice }
+ assert_raise(ArgumentError) { str1.byteslice(1, 2, 3) }
+ assert_raise(TypeError) { str1.byteslice("1") }
+ assert_raise(TypeError) { str1.byteslice("1", 2) }
+ assert_raise(TypeError) { str1.byteslice(1, "2") }
+ assert_raise(TypeError) { str1.byteslice(1..2, 3) }
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal("o", str1.byteslice(4.0))
+ assert_equal("\x82ab", str2.byteslice(2.0, 3.0))
end
diff --git a/test/t/superclass.rb b/test/t/superclass.rb
index 10b6438d3..81630192d 100644
--- a/test/t/superclass.rb
+++ b/test/t/superclass.rb
@@ -23,22 +23,21 @@
[: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
+ [:LocalJumpError, :StandardError, '15.2.25.2'],
+ [:RangeError, :StandardError, '15.2.26.2'],
+ [:RegexpError, :StandardError, '15.2.27.2'],
+ [:RuntimeError, :StandardError, '15.2.28.2'],
+ [:TypeError, :StandardError, '15.2.29.2'],
+ [:ZeroDivisionError, :StandardError, '15.2.30.2'],
[: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'],
+# [:IOError, :StandardError, '15.2.34.2'],
+# [:EOFError, :IOError, '15.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'],
+ [:ScriptError, :Exception, '15.2.37.2'],
+ [:SyntaxError, :ScriptError, '15.2.38.2'],
+# [:LoadError, :ScriptError, '15.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
diff --git a/test/t/symbol.rb b/test/t/symbol.rb
index fdce0f378..4cb210757 100644
--- a/test/t/symbol.rb
+++ b/test/t/symbol.rb
@@ -13,12 +13,8 @@ assert('Symbol', '15.2.11') do
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
+ assert_true :abc === :abc
+ assert_false :abc === :cba
end
assert('Symbol#to_s', '15.2.11.3.3') do
diff --git a/test/t/syntax.rb b/test/t/syntax.rb
index a726719f8..c4c99242b 100644
--- a/test/t/syntax.rb
+++ b/test/t/syntax.rb
@@ -35,9 +35,10 @@ assert('super', '11.3.4') do
end
assert('yield', '11.3.5') do
- assert_raise LocalJumpError do
- yield
- end
+# it's syntax error now
+# assert_raise LocalJumpError do
+# yield
+# end
assert_raise LocalJumpError do
o = Object.new
def o.foo
@@ -357,6 +358,11 @@ assert('splat object in assignment') do
assert_equal [2], (a = *o)
end
+assert('one-line pattern match') do
+ 1 => a
+ assert_equal(1, a)
+end
+
assert('splat object in case statement') do
o = Object.new
def o.to_a
@@ -435,10 +441,11 @@ assert('parenthesed do-block in cmdarg') do
end
assert('method definition in cmdarg') do
- if false
+ result = class MethodDefinitionInCmdarg
+ def self.bar(arg); arg end
bar def foo; self.each do end end
end
- true
+ assert_equal(:foo, result)
end
assert('optional argument in the rhs default expressions') do
@@ -462,6 +469,18 @@ assert('optional block argument in the rhs default expressions') do
assert_nil(Proc.new {|foo = foo| foo}.call)
end
+assert('local variable definition in default value and subsequent arguments') do
+ def m(a = b = 1, c) [a, b, c] end
+ assert_equal([1, 1, :c], m(:c))
+ assert_equal([:a, nil, :c], m(:a, :c))
+
+ def m(a = b = 1, &c) [a, b, c ? true : nil] end
+ assert_equal([1, 1, nil], m)
+ assert_equal([1, 1, true], m{})
+ assert_equal([:a, nil, nil], m(:a))
+ assert_equal([:a, nil, true], m(:a){})
+end
+
assert('multiline comments work correctly') do
=begin
this is a comment with nothing after begin and end
@@ -483,6 +502,10 @@ this is a comment that has extra after =begin and =end with tabs after it
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 }
@@ -661,4 +684,63 @@ assert 'keyword arguments' do
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
+
+ def m(a: b = 1, c:) [a, b, c] end
+ assert_equal([1, 1, :c], m(c: :c))
+ assert_equal([:a, nil, :c], m(a: :a, c: :c))
+end
+
+assert('numbered parameters') do
+ assert_equal(15, [1,2,3,4,5].reduce {_1+_2})
+ assert_equal(45, Proc.new do _1 + _2 + _3 + _4 + _5 + _6 + _7 + _8 + _9 end.call(*[1, 2, 3, 4, 5, 6, 7, 8, 9]))
+end
+
+assert('_0 is not numbered parameter') do
+ _0 = :l
+ assert_equal(:l, ->{_0}.call)
+end
+
+assert('argument forwarding') do
+ c = Class.new {
+ def a0(*a,&b)
+ assert_equal([1,2,3], a)
+ assert_not_nil(b)
+ end
+ def a(...)
+ a0(...)
+ end
+ def b(a,...)
+ assert_equal(a,1)
+ a0(1,...)
+ end
+ }
+ o = c.new
+ o.a(1,2,3){}
+ o.b(1,2,3){}
+end
+
+assert('endless def') do
+ c = Class.new {
+ def m1 = 42
+ def m2() = 42
+ def m3(x) = x+1
+ def self.s1 = 42
+ def self.s2() = 42
+ def self.s3(x) = x + 1
+ def cm1 = m3 42
+ def cm2() = m3 42
+ def cm3(x) = m3 x+1
+ def self.cs1 = s3 42
+ def self.cs2() = s3 42
+ def self.cs3(x) = s3 x + 1
+ }
+ o = c.new
+ assert_equal(42, o.m1)
+ assert_equal(43, o.m3(o.m2))
+ assert_equal(42, c.s1)
+ assert_equal(43, c.s3(c.s2))
+ assert_equal(43, o.cm1)
+ assert_equal(45, o.cm3(o.cm2))
+ assert_equal(43, c.cs1)
+ assert_equal(45, c.cs3(c.cs2))
end
diff --git a/test/t/unicode.rb b/test/t/unicode.rb
index 8622ae08a..c8602da5a 100644
--- a/test/t/unicode.rb
+++ b/test/t/unicode.rb
@@ -1,15 +1,15 @@
# Test of the \u notation
assert('bare \u notation test') do
- # Mininum and maximum one byte characters
+ # Minimum and maximum one byte characters
assert_equal("\x00", "\u0000")
assert_equal("\x7F", "\u007F")
- # Mininum and maximum two byte characters
+ # Minimum and maximum two byte characters
assert_equal("\xC2\x80", "\u0080")
assert_equal("\xDF\xBF", "\u07FF")
- # Mininum and maximum three byte characters
+ # Minimum and maximum three byte characters
assert_equal("\xE0\xA0\x80", "\u0800")
assert_equal("\xEF\xBF\xBF", "\uFFFF")
@@ -17,19 +17,19 @@ assert('bare \u notation test') do
end
assert('braced \u notation test') do
- # Mininum and maximum one byte characters
+ # Minimum and maximum one byte characters
assert_equal("\x00", "\u{0000}")
assert_equal("\x7F", "\u{007F}")
- # Mininum and maximum two byte characters
+ # Minimum and maximum two byte characters
assert_equal("\xC2\x80", "\u{0080}")
assert_equal("\xDF\xBF", "\u{07FF}")
- # Mininum and maximum three byte characters
+ # Minimum 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
+ # Minimum and maximum four byte characters
assert_equal("\xF0\x90\x80\x80", "\u{10000}")
assert_equal("\xF4\x8F\xBF\xBF", "\u{10FFFF}")
end
diff --git a/test/t/vformat.rb b/test/t/vformat.rb
new file mode 100644
index 000000000..f645351ee
--- /dev/null
+++ b/test/t/vformat.rb
@@ -0,0 +1,58 @@
+# coding: utf-8-emacs
+def sclass(v)
+ class << v
+ self
+ end
+end
+
+assert('mrb_vformat') do
+ vf = TestVFormat
+ assert_equal '', vf.z('')
+ assert_equal 'No specifier!', vf.z('No specifier!')
+ assert_equal '`c`: C', vf.c('`c`: %c', ?C)
+ assert_equal '`d`: 123', vf.d('`d`: %d', 123)
+ assert_equal '`d`: -79', vf.d('`d`: %d', -79)
+ assert_equal '`i`: 514', vf.i('`i`: %i', 514)
+ assert_equal '`i`: -83', vf.i('`i`: %i', -83)
+ assert_equal '`t`: NilClass', vf.v('`t`: %t', nil)
+ assert_equal '`t`: FalseClass', vf.v('`t`: %t', false)
+ assert_equal '`t`: TrueClass', vf.v('`t`: %t', true)
+ assert_equal '`t`: Integer', vf.v('`t`: %t', 0)
+ assert_equal '`t`: Hash', vf.v('`t`: %t', {k: "value"})
+ assert_match '#<Class:#<Class:#<Hash:0x*>>>', vf.v('%t', sclass({}))
+ assert_equal 'string and length', vf.l('string %l length', 'andante', 3)
+ assert_equal '`n`: sym', vf.n('`n`: %n', :sym)
+ assert_equal '%C文字列����%', vf.s('%s', '%C文字列����%')
+ assert_equal '`C`: Kernel module', vf.C('`C`: %C module', Kernel)
+ assert_equal '`C`: NilClass', vf.C('`C`: %C', nil.class)
+ assert_match '#<Class:#<String:0x*>>', vf.C('%C', sclass(""))
+ assert_equal '`T`: NilClass', vf.v('`T`: %T', nil)
+ assert_equal '`T`: FalseClass', vf.v('`T`: %T', false)
+ assert_equal '`T`: TrueClass', vf.v('`T`: %T', true)
+ assert_equal '`T`: Integer', vf.v('`T`: %T', 0)
+ assert_equal '`T`: Hash', vf.v('`T`: %T', {k: "value"})
+ assert_match 'Class', vf.v('%T', sclass({}))
+ assert_equal '`Y`: nil', vf.v('`Y`: %Y', nil)
+ assert_equal '`Y`: false', vf.v('`Y`: %Y', false)
+ assert_equal '`Y`: true', vf.v('`Y`: %Y', true)
+ assert_equal '`Y`: Integer', vf.v('`Y`: %Y', 0)
+ assert_equal '`Y`: Hash', vf.v('`Y`: %Y', {k: "value"})
+ assert_equal 'Class', vf.v('%Y', sclass({}))
+ assert_match '#<Class:#<String:0x*>>', vf.v('%v', sclass(""))
+ assert_equal '`v`: 1...3', vf.v('`v`: %v', 1...3)
+ assert_equal '`S`: {:a=>1, "b"=>"c"}', vf.v('`S`: %S', {a: 1, "b" => ?c})
+ assert_equal 'percent: %', vf.z('percent: %%')
+ assert_equal '"I": inspect char', vf.c('%!c: inspect char', ?I)
+ assert_equal '709: inspect mrb_int', vf.i('%!d: inspect mrb_int', 709)
+ assert_equal '"a\x00b\xff"', vf.l('%!l', "a\000b\xFFc\000d", 4)
+ assert_equal ':"&.": inspect symbol', vf.n('%!n: inspect symbol', :'&.')
+ assert_equal 'inspect "String"', vf.v('inspect %!v', 'String')
+ assert_equal 'inspect Array: [1, :x, {}]', vf.v('inspect Array: %!v', [1,:x,{}])
+ assert_match '`!C`: #<Class:0x*>', vf.C('`!C`: %!C', Class.new)
+ assert_equal 'escape: \\%a,b,c,d', vf.v('escape: \\\\\%a,b,\c%v', ',d')
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal '`f`: 0.0125', vf.f('`f`: %f', 0.0125)
+ assert_equal '-Infinity', vf.f('%f', -Float::INFINITY)
+ assert_equal 'NaN: Not a Number', vf.f('%f: Not a Number', Float::NAN)
+end
diff --git a/travis_config.rb b/travis_config.rb
deleted file mode 100644
index 5904d6880..000000000
--- a/travis_config.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-MRuby::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)
- 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